| 概述
MySQL 服务将会维护内存中的host cache ,host cache 包含的信息为:IP,地址,主机名,和错误信息.
host cache 的内容是公开的,可以用select 语句进行查询,以便诊断当前的连接问题.
通过查找缓存找到IP 和主机名的对应结果,可以避免每个clinet 连接时都从DNS 查找.
host cahce 包含有关连接时发生的错误信息.如果错误发生的很频繁,max_connect_errors 将会阻塞这些发生错误的回话的新的链接.
hostcache 将用于 MySQL 使用非本地IP TCP 连接.但是对于环回接口地址是不会建立TCP 链接缓存,套接字也是一样不会使用缓存.
服务将会检查每个新的客户机连接是否在主机缓存中.以便提高链接速度.
查看 hostcache 内容:
select ip,host,host_validated,sum_connect_errors,count_authentication_errors from performance_schema.host_cache;
服务器的缓存内容如下:
当一个tcp client 从指定的IP 地址链接到服务器时,将会创建新的缓存条目记录客户机的IP,主机名,客户机验证白标志.在未链接时,主机名设置为NULL
,标志为false
.这个初始化条目可以用于这个客户端的后续client 链接.
当有一个新的客户端连接进来时,MySQL Server会为这个IP在host cache中建立一个新的记录,包括IP,主机名和client lookup validation flag,分别对应host_cache表中的IP,HOST和HOST_VALIDATED这三列。第一次建立连接因为只有IP,没有主机名,所以HOST将设置为NULL,HOST_VALIDATED将设置为FALSE。
MySQL Server检测HOST_VALIDATED的值,如果为FALSE,它会试图进行DNS解析,如果解析成功,它将更新HOST的值为主机名,并将HOST_VALIDATED值设为TRUE。如果没有解析成功,判断失败的原因是永久的还是临时的,如果是永久的,则HOST的值依旧为NULL,且将HOST_VALIDATED的值设置为TRUE,后续连接不再进行解析,如果该原因是临时的,则HOST_VALIDATED依旧为FALSE,后续连接会再次进行DNS解析。
如果链接时发生错误,服务器会记录错误次数和错误的描述.
服务器使用host cache 时,使用 gethostbyaddr_r()
和 gethostbyname_r()
两个函数,这两个函数调用操作系统支持.如果不调用这两个线程,或者无法调用,线程将会互斥锁.在有互斥锁释放之前,是没有其他线程可以在主机缓存中找不存在于host cache 的主机名.
为了解决阻塞的服务,可以使用FLUSH HOSTS刷新host cache .
如果阻塞的主机没有使用手动flush hosts .被阻塞的主机也有可能解除阻塞.因为当新的client 链接进来后,如果缓存已经满了,服务器将会删除最近最少使用的缓存条目,以便为新的条目腾出空间,如果丢弃的时阻塞主机的,则主机将被解除阻塞.
host cache 默认开启,如果想禁用,使用host cache = 0
如果禁用 DNS 主机名查找功能,使用--skip-name-resove
选项启动服务器.但是注意如果没有指定IP 连接的客户端(例如通过DNS 解析的客户端)则会无法链接.
如果DNS 主机非常慢,可以禁用此项功能 --skip-name-resolve
.或者增大host_cache_size
如果完全禁用TCP/IP 链接,请使用--skip-networing
选项启动服务器.
有些链接错误与 TCP 链接无关,这些错误信息请检查Connection_errors_*_xxx
状态变量
谈谈 host cache的优缺点:
缺点:当有一个新的客户端连接进来时,MySQL Server都要建立一个新的记录,如果DNS解析很慢,无疑会影响性能。如果被允许访问的主机很多,也会影响性能,这个与host_cache_size有关,这个参数是5.6.5引入的。5.6.8之前默认是128,5.6.8之后默认是-1,基于max_connections的值动态调整。所以如果被允许访问的主机很多,基于LRU算法,先前建立的连接可能会被挤掉,这些主机重新进来时,会再次进行DNS查询。
优点:通常情况下,主机名是不变的,而IP是多变的。如果一个客户端的IP经常变化,那基于IP的授权将是一个繁琐的过程。因为你很难确定IP什么时候变化。而基于主机名,只需一次授权。而且,基于host cache中的失败信息,可在一定程度上阻止外界的暴力破解攻击。
关于阻止外界的暴力破解攻击,涉及到max_connect_errors参数,默认为100,官方的解释如下:
If more than this many successive connection requests from a host are interrupted without a successful connection, the server blocks that host from further connections.
如果某个客户端的连接达到了max_connect_errors的限制,将被禁止访问,并提示以下错误:
Host 'host_name' is blocked because of many connection errors.
Unblock with 'mysqladmin flush-hosts'
查看当前的最大链接错误数.
mysql> show variables like 'max_connect_errors';
+--------------------+-------+
| Variable_name | Value |
+--------------------+-------+
| max_connect_errors | 100 |
+--------------------+-------+
1 row in set (0.07 sec)
设置最大连接数错误
set global max_connect_errors=2;
[root@mysql-slave1 ~]# telnet 192.168.244.145 3306
Trying 192.168.244.145...
Connected to 192.168.244.145.
Escape character is '^]'.
N
5.6.26-log
K]qA1nYT!w|+ZhxF1c#|kmysql_native_password
^]
!#08S01Got packets out of orderConnection closed by foreign host.
[root@mysql-slave1 ~]# telnet 192.168.244.145 3306
Trying 192.168.244.145...
Connected to 192.168.244.145.
Escape character is '^]'.
N
Y#>PVB(>!Bl}NKnjIj]sMmysql_native_password
^]
!#08S01Got packets out of orderConnection closed by foreign host.
[root@mysql-slave1 ~]# mysql -h192.168.244.145 -uroot -p123456
Warning: Using a password on the command line interface can be insecure.
ERROR 1129 (HY000): Host '192.168.244.144' is blocked because of many connection errors; unblock with 'mysqladmin flush-hosts'
即便后来使用了正确的账号和密码登录,依旧会被阻止。
mysql> select ip,host,host_validated,sum_connect_errors,count_authentication_errors from performance_schema.host_cache;
+----------------+------+----------------+--------------------+-----------------------------+
| ip | host | host_validated | sum_connect_errors | count_authentication_errors |
+----------------+------+----------------+--------------------+-----------------------------+
| 192.168.10.101 | NULL | YES | 2 | 0 |
| 192.168.10.144 | NULL | YES | 0 | 0 |
+----------------+------+----------------+--------------------+-----------------------------+
2 rows in set (0.00 sec)
mysql> flush hosts;
mysqladmin flush-hosts
truncate table performance_schema.host_cache;
或者等待该记录从host cache中被挤掉。
mysql> select User,Host from mysql.user
-> ;
+------------------+-----------+
| User | Host |
+------------------+-----------+
| dsg | % |
| mysql.infoschema | localhost |
| mysql.session | localhost |
| mysql.sys | localhost |
| root | localhost |
+------------------+-----------+
5 rows in set (0.00 sec)
[root@dbserver ~]# mysql -h127.0.0.1 -u dsg -p
Enter password:
Welcome to the MySQL monitor. Commands end with ; or \g.
Your MySQL connection id is 17
Server version: 8.0.15 MySQL Community Server - GPL
Copyright (c) 2000, 2019, Oracle and/or its affiliates. All rights reserved.
Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.
Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.
mysql> \s
--------------
mysql Ver 8.0.15 for Linux on x86_64 (MySQL Community Server - GPL)
Connection id: 17
Current database:
Current user: dsg@127.0.0.1
SSL: Cipher in use is DHE-RSA-AES128-GCM-SHA256
Current pager: stdout
Using outfile: ''
Using delimiter: ;
Server version: 8.0.15 MySQL Community Server - GPL
Protocol version: 10
Connection: 127.0.0.1 via TCP/IP
Server characterset: utf8mb4
Db characterset: utf8mb4
Client characterset: utf8mb4
Conn. characterset: utf8mb4
TCP port: 3306
Uptime: 6 min 40 sec
Threads: 3 Questions: 29 Slow queries: 0 Opens: 148 Flush tables: 2 Open tables: 124 Queries per second avg: 0.072
--------------
mysql>
以上结果说明 :通过127.0.0.1 是通过TCP/IP 链接.
使用localhost 登录:
[root@dbserver ~]# mysql -hlocalhost -u dsg -p
Enter password:
Welcome to the MySQL monitor. Commands end with ; or \g.
Your MySQL connection id is 18
Server version: 8.0.15 MySQL Community Server - GPL
Copyright (c) 2000, 2019, Oracle and/or its affiliates. All rights reserved.
Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.
Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.
mysql> \s
--------------
mysql Ver 8.0.15 for Linux on x86_64 (MySQL Community Server - GPL)
Connection id: 18
Current database:
Current user: dsg@localhost
SSL: Not in use
Current pager: stdout
Using outfile: ''
Using delimiter: ;
Server version: 8.0.15 MySQL Community Server - GPL
Protocol version: 10
Connection: Localhost via UNIX socket
Server characterset: utf8mb4
Db characterset: utf8mb4
Client characterset: utf8mb4
Conn. characterset: utf8mb4
UNIX socket: /var/lib/mysql/mysql.sock
Uptime: 8 min 25 sec
Threads: 3 Questions: 34 Slow queries: 0 Opens: 148 Flush tables: 2 Open tables: 124 Queries per second avg: 0.067
--------------
mysql>
是通过套接字登录.
[root@dbserver ~]# mysql -h127.0.0.1 -u root -p
Enter password:
ERROR 1045 (28000): Access denied for user 'root'@'127.0.0.1' (using password: YES)
[root@dbserver ~]#
[root@localhost ~]# mysql -uroot -h127.0.0.1 -p123456 -P3306
Warning: Using a password on the command line interface can be insecure.
ERROR 1045 (28000): Access denied for user 'root'@'localhost' (using password: YES)
:::alert-warning
注意: ‘t1’@’%‘中包含’t1’@‘127.0.0.1’,如果开启skip_name_resolve参数,则’t1’@’%‘中定义的密码可用于’t1’@‘127.0.0.1’的登录,如果没有开启该参数,则’t1’@‘127.0.0.1’会转化为’t1’@‘localhost’登录,此时’t1’@’%'定义的密码并不适用。
:::