【GaussDB】使用MySQL客户端连接到GaussDB的M-Compatibility数据库

【GaussDB】使用MySQL客户端连接到GaussDB的M-Compatibility数据库

概述

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兼容模式建库:

sql 复制代码
CREATE DATABASE mysql DBCOMPATIBILITY='M';

修改参数

参考文档

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

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

设置兼容性参数

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

参数配置:

  1. 配置协议监听的TCP端口号,设置GUC参数 plat_compat_server_port 为合理端口
  2. 如果 使用到RSA,需要设置GUC参数 plat_compat_allow_public_key_retrievalon
  3. 将GUC参数 m_format_dev_version 设置为 "s2"

执行命令:

bash 复制代码
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'"

创建连接用户(可选)

sql 复制代码
CREATE USER mysql_user PASSWORD 'Gaussdb@123' SYSADMIN MONADMIN;

连接测试

初始连接问题

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

错误信息: plat_compat_b_conn_handshake, handshake error.

Java代码连接测试

尝试手写一段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();
        }
    }
}

运行结果: 也报错了

java 复制代码
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.<init>(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.<init>(HandshakeContext.java:172)
        at java.base/sun.security.ssl.ClientHandshakeContext.<init>(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开发-开发步骤-连接数据库-以常规方式连接》,发现文档中的连接串是:

java 复制代码
String sourceURL = "jdbc:mysql://$ip:$port/database?useSSL=false&allowPublicKeyRetrieval=true";

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

步骤1:添加 useSSL=false

先加一个 useSSL=false 参数测试。

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

步骤2:添加 allowPublicKeyRetrieval=true

再把另一个参数也加上去 allowPublicKeyRetrieval=true

结果: ✅ 执行成功了!

验证其他客户端

在mogeaver里也加上这个参数 allowPublicKeyRetrieval=true

结果: ✅ 也连接成功了!

说明: 但是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

错误信息: 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. 修改密码加密参数:
bash 复制代码
gs_guc reload -I all -N all -c "password_encryption_type=2"
  1. 重新设置用户密码:
sql 复制代码
ALTER USER mysql_user PASSWORD 'mysql_123';

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


进阶配置:RSA加密支持

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

RSA加密登录配置要求:

  1. 设置 plat_compat_allow_public_key_retrieval 参数值为 on
  2. 设置 require_ssl 参数值为 off
  3. 设置 hba type 参数值为 hosthostnossl

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

连接方式总结

综合分散在文档各个地方的信息来看,连接应该是可以用RSA或者不用RSA:

服务端配置 客户端配置 说明
plat_compat_allow_public_key_retrieval=off useSSL=true&enabledTLSProtocols=TLSv1,TLSv1.1,TLSv1.2 禁用RSA,使用SSL
plat_compat_allow_public_key_retrieval=on useSSL=false&allowPublicKeyRetrieval=true 使用RSA,不使用SSL
plat_compat_allow_public_key_retrieval=on useSSL=true&enabledTLSProtocols=TLSv1,TLSv1.1,TLSv1.2 允许使用RSA,但仍然使用SSL

实测也的确如此。


补充说明

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

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

重要声明

⚠️ 版本说明 :本文这里的这些内容,仅为在 506.0 SPC0100 集中式版本上测试的结果,不代表新版本的表现(目前已知有些东西在新版本改了)。


相关文档

相关推荐
.Shu.1 小时前
Mysql InnoDB 底层架构设计、功能、原理、源码系列合集【五、InnoDB 高阶机制与实战调优】
数据库·mysql
新法国菜2 小时前
MySql知识梳理之DDL语句
数据库·mysql
DarkAthena3 小时前
【GaussDB】全密态等值查询功能测试及全密态技术介绍
数据库·gaussdb
ShawnLeiLei3 小时前
2.3 Flink的核心概念解析
数据库·python·flink
石皮幼鸟4 小时前
数据完整性在所有场景下都很重要吗?
数据库·后端
大只鹅5 小时前
Centos7.9 Docker26容器化部署 MySql9.4 一主一从的同步复制部署
mysql·centos
叁沐5 小时前
MySQL 28 读写分离有哪些坑?
mysql
nightunderblackcat6 小时前
新手向:异步编程入门asyncio最佳实践
前端·数据库·python
livemetee6 小时前
Flink2.0学习笔记:使用HikariCP 自定义sink实现数据库连接池化
大数据·数据库·笔记·学习·flink