概述

GaussDB 从内核 505.1 版本(2024年4月发布)起,新增了一种新的兼容模式 M-Compatibility,这是一种整体上比之前的B模式对MySQL兼容性更高的模式。这种M模式在建库时会自动使用模板库 templatem。

之所以需要再新增一种M模式,我猜测是因为GaussDB发现有些MySQL兼容性已经完全违背了内核的SQL及存储逻辑,无法通过参数调整进行兼容,为了不影响原有B模式的用户,必须新增一种兼容模式来隔离之前的逻辑。

新的这个M兼容模式,可以用MySQL客户端进行连接,但是实际使用过程中,可能会有一些坑在文档中并未详细说明,本文就来介绍一下"使用MySQL客户端连接到GaussDB的M-Compatibility数据库"的正确姿势。

测试环境:本文使用的GaussDB内核版本为 506.0 SPC0100

建库操作
指定M兼容模式建库:

CREATE DATABASE mysql DBCOMPATIBILITY=‘M’;

修改参数

参考文档
《基于MySQL JDBC开发-开发步骤-环境准备》

添加客户端IP到数据库白名单

将客户端IP添加到数据库白名单,具体操作请联系管理员处理。

设置兼容性参数

请参见《管理员指南》中的"配置运行参数"章节,配置相关GUC参数并重启数据库。

参数配置:

1、配置协议监听的TCP端口号,设置GUC参数 plat_compat_server_port 为合理端口
2、如果使用到RSA,需要设置GUC参数 plat_compat_allow_public_key_retrieval 为 on
3、将GUC参数 m_format_dev_version 设置为 “s2”
执行命令:

gs_guc reload -I all -N all -c "plat_compat_server_port=3306"
gs_guc reload -I all -N all -c "plat_compat_allow_public_key_retrieval=on"
gs_guc reload -I all -N all -c "m_format_dev_version='s2'"

创建连接用户(可选)

CREATE USER mysql_user PASSWORD ‘Gaussdb@123’ SYSADMIN MONADMIN;

连接测试

初始连接问题

image-grwl.png

使用MySQL客户端连接时出现报错:

错误信息: plat_compat_b_conn_handshake, handshake error.

Java代码连接测试

尝试手写一段Java代码进行连接:

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.Statement;

public class MySQLCreateTable {
    public static void main(String[] args) {
        String url = "jdbc:mysql://192.168.163.119:3306/mysql";
        String user = "mysql_user";
        String password = "Gaussdb@123";

        try (Connection conn = DriverManager.getConnection(url, user, password);
             Statement stmt = conn.createStatement()) {

            String sql = "CREATE TABLE IF NOT EXISTS users2 (" +
                         "id INT PRIMARY KEY AUTO_INCREMENT," +
                         "name VARCHAR(100) NOT NULL," +
                         "email VARCHAR(100) NOT NULL UNIQUE" +
                         ")";
            stmt.executeUpdate(sql);
            System.out.println("Table created successfully.");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

运行结果: 也报错了

com.mysql.cj.jdbc.exceptions.CommunicationsException: Communications
link failure

The last packet sent successfully to the server was 0 milliseconds
ago. The driver has not received any packets from the server.
at com.mysql.cj.jdbc.exceptions.SQLError.createCommunicationsException(SQLError.java:174)
at com.mysql.cj.jdbc.exceptions.SQLExceptionsMapping.translateException(SQLExceptionsMapping.java:64)
at com.mysql.cj.jdbc.ConnectionImpl.createNewIO(ConnectionImpl.java:827)
at com.mysql.cj.jdbc.ConnectionImpl.(ConnectionImpl.java:447)
at com.mysql.cj.jdbc.ConnectionImpl.getInstance(ConnectionImpl.java:237)
at com.mysql.cj.jdbc.NonRegisteringDriver.connect(NonRegisteringDriver.java:199)
at java.sql/java.sql.DriverManager.getConnection(DriverManager.java:681)
at java.sql/java.sql.DriverManager.getConnection(DriverManager.java:229)
at MySQLCreateTable.main(MySQLCreateTable.java:11) Caused by: com.mysql.cj.exceptions.CJCommunicationsException: Communications link
failure

The last packet sent successfully to the server was 0 milliseconds
ago. The driver has not received any packets from the server.
at java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance0(Native
Method)
at java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:77)
at java.base/jdk.internal.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
at java.base/java.lang.reflect.Constructor.newInstanceWithCaller(Constructor.java:499)
at java.base/java.lang.reflect.Constructor.newInstance(Constructor.java:480)
at com.mysql.cj.exceptions.ExceptionFactory.createException(ExceptionFactory.java:61)
at com.mysql.cj.exceptions.ExceptionFactory.createException(ExceptionFactory.java:105)
at com.mysql.cj.exceptions.ExceptionFactory.createException(ExceptionFactory.java:151)
at com.mysql.cj.exceptions.ExceptionFactory.createCommunicationsException(ExceptionFactory.java:167)
at com.mysql.cj.protocol.a.NativeProtocol.negotiateSSLConnection(NativeProtocol.java:340)
at com.mysql.cj.protocol.a.NativeAuthenticationProvider.negotiateSSLConnection(NativeAuthenticationProvider.java:777)
at com.mysql.cj.protocol.a.NativeAuthenticationProvider.proceedHandshakeWithPluggableAuthentication(NativeAuthenticationProvider.java:486)
at com.mysql.cj.protocol.a.NativeAuthenticationProvider.connect(NativeAuthenticationProvider.java:202)
at com.mysql.cj.protocol.a.NativeProtocol.connect(NativeProtocol.java:1348)
at com.mysql.cj.NativeSession.connect(NativeSession.java:163)
at com.mysql.cj.jdbc.ConnectionImpl.connectOneTryOnly(ConnectionImpl.java:947)
at com.mysql.cj.jdbc.ConnectionImpl.createNewIO(ConnectionImpl.java:817)
… 6 more Caused by: javax.net.ssl.SSLHandshakeException: No appropriate protocol (protocol is disabled or cipher suites are
inappropriate)
at java.base/sun.security.ssl.HandshakeContext.(HandshakeContext.java:172)
at java.base/sun.security.ssl.ClientHandshakeContext.(ClientHandshakeContext.java:103)
at java.base/sun.security.ssl.TransportContext.kickstart(TransportContext.java:240)
at java.base/sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:448)
at java.base/sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:426)
at com.mysql.cj.protocol.ExportControlled.performTlsHandshake(ExportControlled.java:316)
at com.mysql.cj.protocol.StandardSocketFactory.performTlsHandshake(StandardSocketFactory.java:188)
at com.mysql.cj.protocol.a.NativeSocketConnection.performTlsHandshake(NativeSocketConnection.java:99)
at com.mysql.cj.protocol.a.NativeProtocol.negotiateSSLConnection(NativeProtocol.java:331)
… 13 more

解决方案

查看官方文档《基于MySQL JDBC开发-开发步骤-连接数据库-以常规方式连接》,发现文档中的连接串是:

String sourceURL =
“jdbc:mysql://ip:ip:ip:port/database?useSSL=false&allowPublicKeyRetrieval=true”;

多了两个参数,逐步测试:

步骤1:添加 useSSL=false

先加一个 useSSL=false 参数测试。
image-kjpz.png

结果: 出现了和mogeaver一样的报错。

步骤2:添加 allowPublicKeyRetrieval=true

再把另一个参数也加上去 allowPublicKeyRetrieval=true。
image-mrai.png

结果: ✅ 执行成功了!

验证其他客户端

在mogeaver里也加上这个参数 allowPublicKeyRetrieval=true。
image-jlyz.png

结果: ✅ 也连接成功了!

说明:
但是mogeaver里的useSSL为true,之所以没有报错,是因为mogeaver里还自动加上了enabledTLSProtocols=TLSv1,TLSv1.1,TLSv1.2

连接参数总结

根据测试,自己写的Java代码,连接时必须加上:

方案一: useSSL=false&allowPublicKeyRetrieval=true
方案二: useSSL=true&enabledTLSProtocols=TLSv1,TLSv1.1,TLSv1.2

常见问题及解决

如果是普通的测试人员,测到这里,就会认为连接测试这一项已经测试通过了,但是换几个不同的环境里的GaussDB去测,还可能会出现这样的报错:

问题:Access denied for user

image-itil.png

错误信息: Access denied for user

注意: 如果是用户名密码错误,也会报这个错,所以先要用gsql测试一下用户名密码是否正确,确认用户名密码没有写错后,那么问题就可能发生在密码加密方式上。

原因分析

在官方文档[《配置客户端接入认证》](https://support.huawei.com/carrier/docview?nid=DOC1101458028&path=PBI1-253383977/PBI1-23710112/PBI1-23710137/PBI1-256197501&partNo=k00e)这个章节里写着:

外部客户端使用M-Compatibility兼容协议远程连接M-Compatibility数据库时,只支持 sha256认证方式

于是去检查gs_hba.conf,发现无论是配置成md5还是sha256,Access denied for user这个问题都依然存在。

然后继续翻官方文档,发现在《设置密码安全策略》这个章节里写着:

使用M-Compatibility兼容协议连接数据库时,仅支持sha256加密模式,即 password_encryption_type =
2

也就是说,必须确保数据库用户的密码存储时,必须是仅存储sha256密文,不能支持其他方式。

解决步骤

1、修改密码加密参数:

gs_guc reload -I all -N all -c “password_encryption_type=2”

2、重新设置用户密码:

ALTER USER mysql_user PASSWORD ‘mysql_123’;

到此,使用MySQL客户端连接GaussDB就顺利了…吗?

进阶配置:RSA加密支持

搜索官方文档,发现还有这么一个章节:《使用RSA加密登录密码》

RSA加密登录配置要求:

设置 plat_compat_allow_public_key_retrieval 参数值为 on 设置 require_ssl 参数值为
off 设置 hba type 参数值为 host 或 hostnossl

也就是说,如果 require_ssl 为 on,那么本文的配置方式也会连接不上去了。

补充说明

另外,数据库里还有个参数 plat_compat_default_database,可以配置MySQL协议连过来需要连接哪个库。

在没有设置这个参数时: JDBC连接串里的dbname就真的是GaussDB里面的数据库名称
如果设置了这个参数: JDBC连接串里的dbname就是 plat_compat_default_database 指定的库下的schema名称

Logo

助力广东及东莞地区开发者,代码托管、在线学习与竞赛、技术交流与分享、资源共享、职业发展,成为松山湖开发者首选的工作与学习平台

更多推荐