今天要给大家分析另外一个真实的大家都经常会碰到的数据库生产故障,就是数据库无法连接的问题
大家会看到的异常信息往往是“ERROR 1040(HY000): Too many connections”,这个时候就是说数据库的连接池里已经有太多的连接了,不能再跟你建立新的连接了! 不知道大家是否还记得我们最早讲过的数据库的整体架构原理,数据库自己其实是有一个连接池的,你的每个系统部 署在一台机器上的时候,你那台机器上部署的系统实例/服务实例自己也是有一个连接池的,你的系统每个连接Socket 都会对应着数据库连接池里的一个连接Socket,这就是TCP网络连接,如下图所示。
所以当数据库告诉你Too many connections的时候,就是说他的连接池的连接已经满了,你业务系统不能跟他建立更 多的连接了! 曾经在我们的一个生产案例中,数据库部署在64GB的大内存物理机上,机器配置各方面都很高,然后连接这台物理机 的Java系统部署在2台机器上,Java系统设置的连接池的最大大小是200,也就是说每台机器上部署的Java系统,最多 跟MySQL数据库建立200个连接,一共最多建立400个连接,我们看下图示意。
但是这个时候如果MySQL报异常说Too many Connections,就说明目前MySQL甚至都无法建立400个网络连接?这 也太少了吧!毕竟是高配置的数据库机器! 于是我们检查了一下MySQL的配置文件,my.cnf,里面有一个关键的参数是max_connections,就是MySQL能建立 的最大连接数,设置的是800。 那奇怪了,明明设置了MySQL最多可以建立800个连接,为什么居然两台机器要建立400个连接都不行呢? 这个时候我们可以用命令行或者一些管理工具登录到MySQL去,可以执行下面的命令看一下:
show variables like 'max_connections'
此时你可以看到,当前MySQL仅仅只是建立了214个连接而已! 所以我们此时就可以想到,是不是MySQL根本不管我们设置的那个mac_connections,就是直接强行把最大连接数设 置为214了?于是我们可以去检查一下MySQL的启动日志(/var/log/mysqld.log),可以看到如下的字样:
Could not increase number of max_open_files to more than mysqld (request: 65535)
Changed limits: max_connections: 214 (requested 2000)
Changed limits: table_open_cache: 400 (requested 4096)
所以说,看看日志就很清楚了,MySQL发现自己无法设置max_connections为我们期望的800,只能强行限制为214 了! 这是为什么呢?简单来说,就是因为底层的linux操作系统把进程可以打开的文件句柄数限制为了1024了,导致 MySQL最大连接数是214! 可能有的人会疑惑说,为什么linux的文件句柄数量被限制了,MySQL最大连接数就被限制了呢? 其实这个问题你先不用操心了,因为这都是linux的知识,你现在只要知道有这么一件事儿就可以了,看下图的示意。
Version:0.9
今天我们继续讲解昨天的那个案例背景,其实就是经典的Too many connections故障,他的核心就是linux的文件句 柄限制,导致了MySQL的最大连接数被限制,那么今天来讲讲怎么解决这个问题。
其实核心就是一行命令:
ulimit -HSn 65535
然后就可以用如下命令检查最大文件句柄数是否被修改了
cat /etc/security/limits.conf
cat /etc/rc.local
如果都修改好之后,可以在MySQL的my.cnf里确保mac_connections参数也调整好了,然后可以重启服务器,然后重 启MySQL,这样的话,linux的最大文件句柄就会生效了,MySQL的最大连接数也会生效了。
然后此时你再尝试业务系统去连接数据库,就没问题了!
另外再给大家解释一个问题,有人还是疑惑说,为什么linux的最大文件句柄限制为1024的时候,MySQL的最大连接 数是214呢?
这个其实是MySQL源码内部写死的,他在源码中就是有一个计算公式,算下来就是如此罢了!
然后再给大家说一下,这个linux的ulimit命令是干嘛用的,其实说白了,linux的话是默认会限制你每个进程对机器资 源的使用的,包括可以打开的文件句柄的限制,可以打开的子进程数的限制,网络缓存的限制,最大可以锁定的内存 大小。
因为linux操作系统设计的初衷,就是要尽量避免你某个进程一下子耗尽机器上的所有资源,所以他默认都是会做限制 的。
那么对于我们来说,常见的一个问题,其实就是文件句柄的限制。
因为如果linux限制你一个进程的文件句柄太少的话,那么就会导致我们没办法创建大量的网络连接,此时我们的系统 进程就没法正常工作了
举个例子,比如MySQL运行的时候,其实就是linux上的一个进程,那么他其实是需要跟很多业务系统建立大量的连接 的,结果你限制了他的最大文件句柄数量,那么他就不能建立太多连接了!
所以说,往往你在生产环境部署了一个系统,比如数据库系统、消息中间件系统、存储系统、缓存系统之后,都需要 调整一下linux的一些内核参数,这个文件句柄的数量是一定要调整的,通常都得设置为65535
还有比如Kafka之类的消息中间件,在生产环境部署的时候,如果你不优化一些linux内核参数,会导致Kafka可能无法 创建足够的线程,此时也是无法运行的。
所以我们平时可以用ulimit命令来设置每个进程被限制使用的资源量,用ulimit -a就可以看到进程被限制使用的各种资 源的量
比如 core file size 代表的进程崩溃时候的转储文件的大小限制,max locked memory就是最大锁定内存大小,open files就是最大可以打开的文件句柄数量,max user processes就是最多可以拥有的子进程数量。
设置之后,我们要确保变更落地到/etc/security/limits.conf文件里,永久性的设置进程的资源限制
所以执行ulimit -HSn 65535命令后,要用如下命令检查一下是否落地到配置文件里去了。
cat /etc/security/limits.conf
cat /etc/rc.local