连接数据库失败,难道MySQL 5.7客户端与8.0数据库不兼容?

2022-05-19 14:51:42 浏览数 (1)

连接数据库失败,难道MySQL 5.7客户端与8.0数据库不兼容?

  • 前言
  • 1. MySQL 8.0数据库连接失败
  • 2. 连接失败与SSL相关
  • 3. 为什么5.5/5.6连接MySQL 8.0是正常的,偏偏5.7客户端连接失败
    • 3.1 MySQL 5.7客户端的变化
    • 3.2 MySQL 8.0数据库端的变化
  • 4. 为什么5.7客户端连接8.0.28失败,连接8.0.28之前版本数据库正常呢
  • 5. 解决方案

前言

线上业务数据库升级到MySQL 8.0.28之后,业务侧使用MySQL 5.5版本的mysql_api连接数据库正常,但是我们管理端使用旧的MySQL 5.7客户端连接数据库却是失败的。难道MySQL 5.7的客户端与8.0的数据库之间不兼容? 这个问题可就比较严重了,可能成为数据库升级路上的拦路虎。一下就勾起了吹水老王极大的兴致,我们一起来分析一下。

1. MySQL 8.0数据库连接失败

我们线上将一套数据库从MySQL 5.7.26升级到MySQL 8.0.28之后,业务侧有两种程序语言,c 程序通过MYSQL_API连接数据库,java程序通过JDBC连接数据库,升级之后没有任何异常;但是,我们DB管理端的监控通过原有的MySQL 5.7客户端连接数据库失败。

JDBC与这个问题不相关,我们暂且抛去不谈;现在问题就是,MYSQL_API使用的MySQL 5.5客户端可以正常连接8.0.28数据库,MySQL 5.7客户端却不能。

为了明确具体问题,我们先做个测试,分别使用MySQL 5.5/5.6/5.7的客户端连接MySQL 8.0数据库。

代码语言:javascript复制
# 使用MySQL 5.7的客户端连接MySQL 8.0.28时报错
# /usr/local/mysql/bin/mysql --version
/usr/local/mysql/bin/mysql  Ver 14.14 Distrib 5.7.26, for el7 (x86_64) using  EditLine wrapper

# /usr/local/mysql/bin/mysql -h9.135.1.1 -P3307 -utest -ptest
mysql: [Warning] Using a password on the command line interface can be insecure.
ERROR 2026 (HY000): SSL connection error: unknown error number
代码语言:javascript复制
# 使用MySQL 5.5的客户端连接MySQL 8.0.28,连接成功。
# /bin/mysql --version
/bin/mysql.bak  Ver 15.1 Distrib 5.5.68-MariaDB, for Linux (x86_64) using readline 5.1

# /bin/mysql -h9.135.1.1 -P3307 -utest -ptest
Welcome to the MariaDB monitor.  Commands end with ; or g.
Your MySQL connection id is 12
Server version: 8.0.28 MySQL Community Server - GPL
Copyright (c) 2000, 2018, Oracle, MariaDB Corporation Ab and others.
Type 'help;' or 'h' for help. Type 'c' to clear the current input statement.

MySQL [(none)]> select version();

最终,得到以下结果:

  1. 使用MySQL 5.7的客户端或者mysql_api连接MySQL 8.0.28以上版本的数据库时失败;
  2. 而使用MySQL 5.5和5.6版本的客户端连接MySQL 8.0.28以上版本数据库却是正常的;
  3. 使用MySQL 5.5/5.6/5.7版本的客户端连接MySQL 8.0.28以下版本(如MySQL 8.0.23)都是正常的。

这就不得不让人怀疑,难道5.7版本的MySQL客户端与8.0版本数据库是否存在兼容性问题?

2. 连接失败与SSL相关

首先,报错信息为SSL connection error,可以猜想使用MySQL 5.7的客户端连接MySQL 8.0数据库时的报错与SSL相关。

我们来回忆一下数据库连接基础知识。 MySQL数据库的连接方式通常有两种,一种是通过TCP/IP连接,即通过数据库的端口进行通信,另外一种方式是通过本地SOCKET连接,即在客户端在数据库本机通过本地的socket文件连接数据库。SOCKET连接比TCP/IP连接方式要快,但是只适用于客户端和服务端在同一台unix主机的场景。

我们可以验证一下,MySQL 5.7的客户端使用socket方式连接MySQL 8.0数据库是否正常。

代码语言:javascript复制
# 这里我们使用root@localhost用户连接数据库

# /usr/local/mysql/bin/mysql --version
/usr/local/mysql/bin/mysql  Ver 14.14 Distrib 5.7.26, for el7 (x86_64) using  EditLine wrapper

# /usr/local/mysql/bin/mysql -uroot -proot -S /data/MySQLData_3307/data/mysql.sock

Server version:8.0.28 MySQL Community Server - GPL
Copyright (c) 2000, 2019, Oracle and/or its affiliates. All rights reserved.
mysql> select version();
 ----------- 
| version() |
 ----------- 
| 8.0.28    |
 ----------- 
1 row in set (0.00 sec)

可以看到,使用MySQL 5.7的客户端通过本地socket的方式可以正常连接MySQL 8.0.28以上版本的数据库。那么也就证明了5.7版本的客户端与8.0版本的数据库本身其实不存在兼容性问题。MySQL 5.7客户端连接失败只是与TCP的SSL加密连接相关。

3. 为什么5.5/5.6连接MySQL 8.0是正常的,偏偏5.7客户端连接失败

3.1 MySQL 5.7客户端的变化

MySQL 5.7客户端连接8.0数据库失败与SSL加密连接相关。实际上在我们的生产环境,MySQL客户端或者mysql_api连接数据库通常是不使用SSL的。既然MySQL 5.7的客户端与MySQL 5.5/5.6客户端在SSL方面的行为有差异,我们就来看看两者的默认行为有什么不同。

使用mysql --help查看MySQL客户端帮助中与SSL相关的配置,可以看到:MySQL 5.5客户端默认关闭了SSL,而在MySQL 5.7的客户端中默认是开启SSL的,并且多了一个ssl-mode参数配置SSL连接模式。

代码语言:javascript复制
# MySQL 5.5 客户端默认关闭SSL
# /bin/mysql.bak --help | grep ssl
  --ssl               Enable SSL for connection (automatically enabled with
ssl                               FALSE
代码语言:javascript复制
# MySQL 5.7 客户端默认开启SSL
# /usr/local/mysql/bin/mysql --help | grep ssl
  --ssl-mode=name     SSL connection mode.
  --ssl               Deprecated. Use --ssl-mode instead.
                      (Defaults to on; use --skip-ssl to disable.)
ssl                               TRUE

既然SSL配置在MySQL 5.7和之前版本的客户端中存在差异,那么我们可以再验证一下,MySQL 5.7在显式关闭SSL的情况下是否能够连接MySQL 8.0成功。

代码语言:javascript复制
# /usr/local/mysql/bin/mysql --skip-ssl -h9.135.1.1 -P3307 -utest -ptest
Server version: 8.0.28 MySQL Community Server - GPL
Copyright (c) 2000, 2019, Oracle and/or its affiliates. All rights reserved.
Type 'help;' or 'h' for help. Type 'c' to clear the current input statement.

mysql> select version();
 ----------- 
| version() |
 ----------- 
| 8.0.28    |
 ----------- 
1 row in set (0.00 sec)

果然,在显式设置–skip-ssl禁用SSL的情况下,MySQL 5.7的客户端连接MySQL 8.0也是成功的。

至于MySQL 5.7的客户端SSL配置的变更,我们可以通过官方文档 配置MySQL使用加密连接 来确认一下。 By default, MySQL client programs attempt to establish an encrypted connection if the server supports encrypted connections, with further control available through the --ssl-mode option:

  • In the absence of an --ssl-mode option, clients attempt to connect using encryption, falling back to an unencrypted connection if an encrypted connection cannot be established. This is also the behavior with an explicit --ssl-mode=PREFERRED option.

根据MySQL 5.7的文档描述,可以看出,MySQL 5.7的客户端如果没有显式配置–ssl-mode参数,并且数据库端支持SSL加密连接的话,那么客户端会尝试创建SSL加密连接;如果SSL加密连接失败则退而求其次使用不加密连接。

我们注意到,MySQL 5.7的客户端创建SSL加密连接有两个必要条件:1)客户端没有显式禁用SSL;2)数据库端支持SSL加密连接。

3.2 MySQL 8.0数据库端的变化

第一点已经很明确了,MySQL 5.7的客户端默认是开启SSL的。我们来看看第二点,数据库端支持SSL加密连接。

通过对比,我们发现MySQL 8.0 与 MySQL 5.7数据库端的默认配置也存在差异。MySQL 5.7的数据库中默认是禁用SSL的,而在MySQL 8.0中默认是开启SSL的。

代码语言:javascript复制
# MySQL 5.7数据库中默认是禁用SSL的
mysql> show global variables like '%ssl%';
 --------------- ---------- 
| Variable_name | Value    |
 --------------- ---------- 
| have_openssl  | DISABLED |
| have_ssl      | DISABLED |
代码语言:javascript复制
# MySQL 8.0数据库中默认是启用SSL的
mysql> show global variables like '%ssl%';
 ------------------------------------- ----------------- 
| Variable_name                       | Value           |
 ------------------------------------- ----------------- 
| have_openssl                        | YES             |
| have_ssl                            | YES             |

小结:MySQL 8.0数据库初始化时默认开启了SSL,之前版本的数据库默认是禁用SSL的。同时,MySQL 5.7之前版本的客户端默认禁用SSL,而MySQL 5.7版本的客户端在数据库端支持SSL的情况下会尝试创建SSL加密连接。所以,MySQL 5.5/5.6版本客户端不使用加密连接MySQL 8.0数据库,连接是成功的;而MySQL 5.7版本客户端使用SSL加密连接MySQL 8.0数据库,连接失败。


4. 为什么5.7客户端连接8.0.28失败,连接8.0.28之前版本数据库正常呢

这是由于在MySQL 8.0.28版本开始,数据库层默认的tls版本为TLSv1.2,并且不再支持旧版本的TLSv1和TLSv1.1连接;而在之前版本中的默认tls版本为TLSv1。

参考文档:Changes in MySQL 8.0.28

From MySQL 8.0.28, client programs, including MySQL Shell, that support a --tls-version option for specifying TLS protocols for connections to the MySQL server cannot make a TLS/SSL connection with the protocol set to TLSv1 or TLSv1.1. If a client attempts to connect using these protocols, for TCP connections, the connection fails, and an error is returned to the client.

验证一下,对比MySQL 8.0.28数据库和MySQL 5.7客户端支持的TLS_VERSION。

代码语言:javascript复制
# MySQL 8.0.28数据库中默认tls_version
mysql> show global variables like '%tls%';
 ------------------------ ----------------- 
| Variable_name          | Value           |
 ------------------------ ----------------- 
| admin_tls_version      | TLSv1.2,TLSv1.3 |
| tls_version            | TLSv1.2,TLSv1.3 |
 ------------------------ ----------------- 
代码语言:javascript复制
# MySQL 5.7客户端支持的tls_version
# /usr/local/mysql/bin/mysql --help | grep tls
--tls-version=name  TLS version to use, permitted values are: TLSv1, TLSv1.1

5. 解决方案

经过老王一番大胆设想小心求证,问题原因已经显而易见。综上,数据库端MySQL 8.0初始化时默认启用了SSL,并且从MySQL 8.0.28版本开始将默认TLS版本升级为TLSV1.2,并且不再支持旧版本的TLS;而MySQL 5.7版本的客户端包括mysql_api默认会尝试与数据库端以TLSV1或TLSV1.1版本的SSL建立加密连接,TLS版本在数据库层和客户端之间的不兼容导致MySQL 5.7的客户端无法连接MySQL 8.0.28以上数据库。

MySQL 5.7的客户端与8.0数据库本身并不存在兼容性问题,只是SSL版本不兼容而已。解决方案也就很简单了,要么在数据库层禁用SSL,要么在低版本客户端中禁用SSL即可。

0 人点赞