连接MySQ时报错:Host X is blocked because of many connection errors; unblock with 'mysqladmin flush-hosts'

今天来了解一下performance_schema库中的 host_cache表。这个表记录了 host 的缓存内容,缓存信息包括主机名和 IP,用于避免 DNS 查找。这个表的记录,可以在需要的时候协助排查连接问题。

要开启这个功能,要保障参数 performance_schema的状态 是开启的。

另外,MySQL 启动时若带有参数–skip-name-resolve,或者配置文件里有参数skip-name-resolve,则,即使performance_schema=on,表 host_cache 也不会缓存主机信息。

 

前几天碰到一个问题,主要就是这个功能的基础导致的。

问题:

昨天开发同事在使用 MySQL 连接数据库的时候,客户端无法连接上数据库。当时数据库的时候报错如下:

Caused by: com.mysql.cj.exceptions.CJException: null,  message from server: "Host ' xxx' is blocked because of many connection errors; unblock with 'mysqladmin flush-hosts'"

Caused by: com.mysql.cj.exceptions.CJException: null,  message from server: "Host 'xxx' is blocked because of many connection errors; unblock with 'mysqladmin flush-hosts'" at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method) ~[?:1.8.0_66]

解决办法:

在调整 max_connect_erros 从 100 到 300以后,能连上数据库了:

mysql> show variables like '%max%connect%er%';
+--------------------+-------+
| Variable_name      | Value |
+--------------------+-------+
| max_connect_errors | 300   |
+--------------------+-------+
1 row in set (0.06 sec)

这是一个治标不治本的临时解决办法。

 

果然接着发生问题:

过了一段时间,开发同事表示应用服务器又连不上数据库了。报错如下:

Failed to initialize pool: Could not create connection to database server. Attempted reconnect 3 times. Giving up.
2019-10-21 10:26:03.021 main      
         ERROR o.s.b.SpringApplication 842 reportFailure - Application run failed org.springframework.context.
         ApplicationContextException: Unable to start web server;
nested exception is org.springframework.boot.web.server.WebServerException: Unable to start embedded Tomcat
……
Caused by: com.mysql.cj.exceptions.CJException: null,  message from server: "Host '92.168.225.131' is blocked because of many connection errors; unblock with 'mysqladmin flush-hosts'"

解决办法:

最终处理办法,方法有多种:

mysql> flush hosts;
## 或者
mysql> truncate Performance_chema.host_cache;

或者找到mysqladmin

[root@my571 ~]#  whereis mysqladmin
mysqladmin: /usr/bin/mysqladmin /usr/share/man/man1/mysqladmin.1.gz
[root@my571 ~]# /usr/bin/mysqladmin flush-hosts  #清除hosts缓存

或者调整参数:

root@localhost|performance_schema> set global host_cache_size=0;

原因:

当时应用报这种错误的原因,其实是应用到 MySQL 服务器的网络延迟太大:

64 bytes from xxx: icmp_seq=50 ttl=64 time=24031 ms
64 bytes from xxx: icmp_seq=51 ttl=64 time=23008 ms
64 bytes from xxx: icmp_seq=52 ttl=64 time=21984 ms
64 bytes from xxx: icmp_seq=53 ttl=64 time=20960 ms
64 bytes from xxx: icmp_seq=54 ttl=64 time=19936 ms
64 bytes from xxx: icmp_seq=55 ttl=64 time=18912 ms
64 bytes from xxx: icmp_seq=56 ttl=64 time=17888 ms
64 bytes from xxx: icmp_seq=57 ttl=64 time=16864 ms
64 bytes from xxx: icmp_seq=58 ttl=64 time=15840 ms
64 bytes from xxx: icmp_seq=59 ttl=64 time=14815 ms

 

跟这种情况的报错的相关参数:max_connect_errors & connect_timeout。

数据库的connect_timeout = 10,网络延迟基本在 14 秒以上。参数max_connect_errors控制同一个 IP连接连续错误次数多过参数max_connect_errors的值时,这之后的连接mysql会阻止。

# 原文参考地址:

https://dev.mysql.com/doc/refman/5.6/en/blocked-host.html

The value of the max_connect_errors system variable determines how many successive interrupted connection requests are permitted. 

After max_connect_errors failed requests without a successful connection, mysqld assumes that something is wrong (for example, that someone is trying to break in),

 

解决这种问题的方法通常有以下几种:

1、调大 max_connect_errors;

2、flush hosts 或 mysqladmin flush-hosts 或者 truncate host_cache 表;

3、变量 host_cache_size 置为零。

方法一:只是延迟了这个错误再触发的阈值,当此 IP 再次连接 MySQL 失败次数达到参数max_connect_errors的新值,又会报同样的错误了。

方法二:是清理掉 MySQL 中记录的对应的IP主机相关cache记录,从零开始,达到 max_connect_errors 值大小,依旧会报错。

方法三:是临时禁用了host 的缓存功能,只是不记录 host cache 了,每次客户端连接,都会执行 DNS 查找。

# host  cache相关介绍官档连接
https://dev.mysql.com/doc/refman/5.7/en/host-cache.html

官方资料介绍,host_cache的字段SUM_CONNECT_ERRORS是统计被视为“阻塞”的连接错误的数量(根据max_connect_errors系统变量进行评估)。 只计算协议握手错误,并且仅用于通过验证的主机(HOST_VALIDATED = YES)。

MySQL客户端与数据库建立连接需要发起三次握手协议,正常情况下,这个时间非常短,但是一旦网络异常,网络超时等因素出现,就会导致这个握手协议无法完成,

MySQL有个参数connect_timeout,它是MySQL服务端进程mysqld等待连接建立完成的时间,单位为秒。如果超过connect_timeout时间范围内,仍然无法完成协议握手话,MySQL客户端会收到异常,异常消息类似于: Lost connection to MySQL server at 'XXX', system error: errno,该变量默认是10秒。

参数--skip-host-cache的功能类似于 设置参数 host_cache_size=0,但是 host_cache_size更灵活。 host_cache_size能在 MySQL运行时通过修改全局参数调整是否缓存、缓存的大小。

即使启动 MySQL 服务时,加上了--skip-host-cache参数,在数据库运行时也是可以如常修改参数host_cache_size的值,只不过修改不会生效。

 

 

以下来验证 host_cache 中 sum_connect_errors 列的值与 max_connect_errors 参数:

1、主机名my 571 的为 MySQL 服务器,IP:131;主机名为 my80的做应用客户端,IP:129。

构造网络延迟的场景验证:

[root@my80 ~]# tc qdisc add dev eth0 root netem delay 11000ms

2、此时从 my80 上 ping MySQL 服务器:

[root@my80 ~]#  ping 192.168.225.131

PING 192.168.225.131 (192.168.225.131) 56(84) bytes of data.
64 bytes from 192.168.225.131: icmp_seq=1 ttl=64 time=11000 ms
64 bytes from 192.168.225.131: icmp_seq=2 ttl=64 time=11001 ms
64 bytes from 192.168.225.131: icmp_seq=3 ttl=64 time=11001 ms
64 bytes from 192.168.225.131: icmp_seq=4 ttl=64 time=11001 ms
64 bytes from 192.168.225.131: icmp_seq=6 ttl=64 time=11001 ms
64 bytes from 192.168.225.131: icmp_seq=7 ttl=64 time=11001 ms
64 bytes from 192.168.225.131: icmp_seq=5 ttl=64 time=11001 ms
64 bytes from 192.168.225.131: icmp_seq=6 ttl=64 time=11001 ms

延迟基本是 11 秒左右,目前 my571 的数据库的参数 connect_timeout =10,连接是会超时的。

3、my80 上连接 MySQL 服务器,仅执行一次:

[root@my80 ~]# mysql -h192.168.225.131 -uroot -proot
mysql: [Warning] Using a password on the command line interface can be insecure.
ERROR 2026 (HY000): SSL connection error: error:00000000:lib(0):func(0):reason(0)
[root@my80 ~]#

4、查看此时的host_cache 表,结果如下:

此时:sum_connect_errors=1,count_handshake_errors=1。

5、此时继续在my80 上反复登录3次,报错如下:

[root@my80 ~]# mysql -h192.168.225.131 -uroot -proot
mysql: [Warning] Using a password on the command line interface can be insecure.
ERROR 2026 (HY000): SSL connection error: error:00000000:lib(0):func(0):reason(0)
[root@my80 ~]#
[root@my80 ~]# mysql -h192.168.225.131 -uroot -proot
mysql: [Warning] Using a password on the command line interface can be insecure.
ERROR 2026 (HY000): SSL connection error: error:00000000:lib(0):func(0):reason(0)
[root@my80 ~]#
[root@my80 ~]# mysql -h192.168.225.131 -uroot -proot
mysql: [Warning] Using a password on the command line interface can be insecure.
ERROR 2026 (HY000): SSL connection error: error:00000000:lib(0):func(0):reason(0)
[root@my80 ~]#

虽然报错连不上,但是还未出现“ Host '192.168.225.129' is blocked ”。因为当前连续的失败连接数未草果参数 max_connect_errors 的值大小。

此时日志报错信息如下:

2019-10-22T07:51:30.427453Z 11 [Note] Got timeout reading communication packets

6、查看当前参数 max_connect_errors=1+3;当前对应的 host_cache 表记录,如下:

由于参数 connect_timeout=10,网络延时超过10秒,导致连接MySQL失败。

在新增一次失败连接后,MySQL服务器上查询host_cache表的话,就会看到SUM_CONNECT_ERRORS变大 了,在原来的值上加 1,COUNT_HANDSHAKE_ERRORS也变大了1。

7、继续执行,再进行 3 次:

可以看到这次的“三次”连接都报错:“ Host '192.168.225.129' is blocked ”了。

此时仍然,sum_connect_errors=4 && count_handshake_errors=4,均等于 max_connect_errors 参数的设定值,大于这个值的连接都会被拒绝掉,sum_connect_errors 不会变更大,后面三次被拒绝因此报错 “ Host '192.168.225.129' is blocked ”。

此报错的解决办法:

8、然后再在 my80 上尝试登录 my571 的 MySQL:

虽然由于延迟变大,超过 mysql571 的connect_timeout 的限制,但是并未再报错“host xxx is blocked” 了。解决延迟后,就可以继续正常连接数据库了。

9、测试完成后,记得解除延迟 10s 的设置:

[root@my80 ~]# tc qdisc del dev ens33 root netem delay 11000ms


 

额外,参数 count_authentication_errors。

另外,从上面测试案例中看到的 host_cache 表中有一个字段“count_authentication_errors”,这个字段跟 sum_connect_errors 有的人可能会弄混淆。count_authentication_errors 记录的是来自客户端的连接中、输入密码错误的次数。

 

开始测试连接 MySQL 数据库中 count_authentication_errors的变化:

1、先清空 host_cache 的记录:

2、在 my80 上,执行多次错入的密码的数据库连接命令:

3、在 my571 上进入数据库查看表 host_cache:

此时,count_authentication_errors=7,

4、此时数据库相对的错误日志如下:

2019-10-13T07:37:25.282600Z 3 [Warning] IP address '192.168.225.129' could not be resolved: Name or service not known
2019-10-13T07:37:25.308431Z 3 [Note] Access denied for user 'iris'@'192.168.225.129' (using password: YES)
2019-10-13T07:37:26.821914Z 4 [Note] Access denied for user 'iris'@'192.168.225.129' (using password: YES)
2019-10-13T07:37:29.060450Z 5 [Note] Access denied for user 'iris'@'192.168.225.129' (using password: YES)
2019-10-13T07:37:31.075948Z 6 [Note] Access denied for user 'iris'@'192.168.225.129' (using password: YES)
2019-10-13T07:37:32.626528Z 7 [Note] Access denied for user 'iris'@'192.168.225.129' (using password: YES)
2019-10-13T07:37:34.258874Z 8 [Note] Access denied for user 'iris'@'192.168.225.129' (using password: YES)
2019-10-13T07:37:36.540025Z 9 [Note] Access denied for user 'iris'@'192.168.225.129' (using password: YES)

 

总结

本次我们环境的数据库报错,是因为网络环境不好,导致完成连接数据库的时间(即三次握手协议完成的时长)超过MySQL 中定义的参数的 connect_timeout 的值,应用服务器不断尝试连接,最终尝试次数超过了 MySQL 的参数 max_connect_errors 的大小,导致的报错。

参数max_connect_errors决定了 MySQL 允许的每个 IP 持续连接数据库时发生连接错误的次数,对应的是表 host_cache 中的字段 sum_connect_errors 的值的大小,同时也跟 count_handshake_errors 相关;

表host_cache中字段 count_authentication_errors 的值大小,表示连接数据库时使用了错误的密码的次数。目前好像没有参数,可以使用 MySQL 插件控制。

 

 

 

 

### 解决 MySQL 连接错误 08001 'Public Key Retrieval is not allowed' 此错误通常发生在尝试连接MySQL 8.0 或更新版本,因为这些版本默认使用 `caching_sha2_password` 身份验证插件。要解决这个问题,有几种方法: #### 方法一:修改连接 URL 可以在 JDBC 连接字符串中添加参数来允许公钥检索。具体来说,在连接 URL 的末尾加上 `&allowPublicKeyRetrieval=true` 即可[^5]。 对于 Spring Boot 应用程序而言,则是在配置文件中的数据源 URL 后面追加该参数: ```properties spring.datasource.url=jdbc:mysql://localhost:3306/dbname?useSSL=false&serverTimezone=UTC&allowPublicKeyRetrieval=true ``` #### 方法二:更改用户账户的身份验证插件 通过执行 SQL 命令将特定用户的认证方式更改为旧版的 `mysql_native_password` 插件: ```sql ALTER USER 'your_username'@'%' IDENTIFIED WITH mysql_native_password BY 'your_password'; FLUSH PRIVILEGES; ``` 这一步骤会使得所选用户不再依赖于新的缓存 SHA-2 密码机制来进行身份验证[^3]。 #### 方法三:调整 MySQL 配置文件 如果希望全局改变服务器的行为而不是针对单个客户端的话,还可以编辑 MySQL 的配置文件 (my.cnf 或 my.ini),找到 `[mysqld]` 段并加入下面这一行以设定默认的身份验证插件为 `mysql_native_password`: ```ini default_authentication_plugin=mysql_native_password ``` 保存更改后重启 MySQL 服务使其生效。 以上三种办法都可以有效处理因启用 SSL/TLS 加密通信而导致的 “Public Key Retrieval is not allowed” 错误消息。选择最适合当前环境的方法实施即可解决问题。
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值