我们正在三台服务器上运行一个网站。其中两台是负载平衡的 Web 服务器,最后一台是专用的 mysql 服务器。mysql 服务器运行的是 RHEL5 64 位,使用 2.6.18-92.1.6.el5 #1 SMP 内核和 MySQL 5.0.45。这也是一台相当强大的服务器,配有 Xeon L5420 和 8 GB 的 RAM。我们的 Web 服务器的 php 页面配置为使用 mysqli。
通常情况下,我们不会真正利用所有资源,白天我们每秒会进行 20-25 次查询。然而,时不时地,我们会达到最大数据库连接数限制,然后我们的网站就会崩溃。更重要的是,这种情况似乎发生在晚上,而此时网站的流量应该最低。
我们一开始的最大连接数是 100,后来增加到 300,但这种情况仍然会发生。如果说有什么不同的话,那就是我们注意到,有时有大量休眠的 MySQL 进程,但连接到数据库的进程都没有使用持久连接。这种情况并不是每晚都会发生,我们遇到过一些问题,它每晚都会崩溃,然后大约一个半星期就好了,直到今天。
我们没有任何会一次占用数据库几分钟的庞大查询。我们尝试浏览 SLOW_QUERY 日志。我们有几个查询出现在其中,但通常它们不会持续超过 1 或 2 秒,而且这些查询很少发生。
这听起来有什么特别之处吗?接下来我们如何诊断问题?
答案1
我猜想是您的应用程序中有一些长时间运行的查询。执行这些查询时,它们会导致连接长时间处于池外状态(相对于通常的使用模式),这会导致您的池耗尽、增长并继续增长到最大值,此时任何剩余的工作程序都会阻塞,等待连接释放。
首先要追查什么时候这种情况发生时,也就是说,这是一个周期性事件,还是随机事件。如果是前者,那么你很幸运,因为你可以随时做好准备。如果你不能确定模式,那么你就必须保持警惕。
您可能能够通过查看您的网站监控日志或sar
从您的数据库中查看是否存在任何相关的峰值来弄清楚这一点。
如果你可以在数据库负载过大时捕获它,则应在 mysql 服务器上执行以下命令
show innodb status;
show processlist;
前者将打印出有关 innodb 引擎的诊断信息(您使用的是 innodb 对吗?),后者将打印出正在执行的查询的前几百个字符。查找已运行很长时间的查询、在磁盘上生成临时表的查询以及被资源阻塞的查询。
之后,艰苦的工作就开始了。使用EXPLAIN
来估计查询的成本及其使用的资源。避免需要通过临时表在磁盘上排序的查询。查找长期运行的报告作业或其他定期锁定或饱和数据库的计划维护任务。它可以是像备份任务这样简单的任务,也可以是汇总旧采购订单数据的作业。
我建议你在你的/etc/my.cnf
log_slow_queries
log-queries-not-using-indexes
set-variable = long_query_time=1
对于每秒执行 20-30 个请求的 Web 应用程序,您不能承受这些日志中出现任何内容。
顺便说一句,在我看来,将连接池的大小增加到超出原始大小是没有意义的,因为这只会将池耗尽的发生延迟几秒钟,并且只会在不需要时对数据库施加更多压力。
答案2
我以前见过这个。
我们有一个 cron 来对包含 MyISM 表的数据库进行 mysqldump。由于 MyISM,mysql dump 会锁定整个表。导致查询(以及连接)排队。
答案3
AlexMax,您能解决这个问题吗?虽然存在细微的差别,但我目前看到的与您在此处描述的类似问题,即 14 个 Web 服务器负载平衡到 8 个 mysql 服务器(每个网站都编码到其中一个 Web 服务器)。连接数将激增,mysql 进程列表中 90% 以上的连接被列为休眠状态,Query 为 NULL。这将使 mysql 停止允许连接,并持续约 2-3 分钟。四核 Xeon 上的 Mysql 5.0.70 和 PHP 5.28(对我们来说是 32 位)。