为什么MySQL查询堆积在“发送数据”状态?

我们使用InnoDB表作为Web应用程序的后端,一切都很好,大概两年,直到几周前我们不得不重新启动MySQL。 (尽pipe我们并没有真正使用它们,但是我们的主机系统突然停止了对这些请求的响应,但是我们并没有禁止反向DNS查找,现在它们被禁用了。)不幸的是,configuration文件已经改变了, t有一个原始状态的副本作比较。

解决最重要的问题后,我们留下了一个真正的困惑:在高负载下,数据库查询开始时间比平常长。 在这个时候,我们有七个Apache服务器有数百个开放连接。 运行SHOW PROCESSLIST显示,有一半或更多的连接处于“正在发送数据”状态,通常会有几百秒的时间。 几乎所有的查询都是SELECT,类似的查询趋于一致。 事实上,列表中最低的簇往往是完全相同的查询(我期望它在查询caching中),每个返回1104行两个整数。 其他频繁的罪犯是几百个单一整数行,几个单一整数行,甚至一个COUNT(*)结果的列表。

我们尝试在这些时段之一closuresWeb服务器,但是在重新启动后一分钟内就会返回问题。 但是,彻底重新启动mysqld解决了问题,直到第二天。 问题是什么,我们如何validation和/或修复?

那么,请注意,如果我记得(自从我做数据库工作已经有一段时间了),在innodb表上没有WHERE子句的COUNT(*)查询比在MyISAM和Memory表上慢得多。

另外,这是由任何一个Xen DomU的机会?

什么是前端语言? 如果是PHP,是使用MySQL还是MySQLi? 他们是否使用持续连接?

你没有提到底层的操作系统,但是在Linux的情况下,我会首先盯着free -m的输出,特别注意最后两行,看看整个内存是否紧张。

 [0:504] callisto:cyanotype $ free -m total used free shared buffers cached Mem: 3961 3816 144 0 184 1454 -/+ buffers/cache: 2177 1784 Swap: 2898 0 2898 

在这里,我们有一个健康的系统(这是我的工作站)。 第二列不包括caching和caching,所以我实际上使用了2177mb的内存,并有1784兆字节的随时可用。

最后一行显示目前为止我没有使用swap。

然后给vmstat(8) ,看看你的系统是否像疯了一样被摧毁也是有用的。

 [0:505] callisto:cyanotype $ vmstat 5 10 procs -----------memory---------- ---swap-- -----io---- -system-- ----cpu---- rb swpd free buff cache si so bi bo in cs us sy id wa 0 0 0 134116 189828 1499948 0 0 11 3 44 49 1 1 98 0 0 0 0 143112 189836 1489688 0 0 0 6 526 2177 1 1 98 0 0 0 0 139268 190504 1491864 0 0 512 4 663 4704 2 1 96 1 2 0 0 136688 191084 1493484 0 0 473 5 641 3039 1 1 97 1 0 0 0 52636 191712 1518620 0 0 5066 4 1321 6600 8 2 86 4 5 0 0 72992 193264 1377324 0 0 10742 31 1602 7441 12 3 80 5 2 1 0 84036 193896 1202012 0 0 10126 43 2621 4305 31 2 57 10 3 0 0 42456 195812 1060904 0 0 3970 75 55327 9806 43 5 41 10 8 1 0 34620 197040 942940 0 0 3554 64 50892 12531 43 6 44 6 ^C [0:506] callisto:cyanotype $ 

(我的桌面真的没有这么做,对不起,多么浪费8个完美的核心)

如果你在'b'栏中看到很多stream程花费时间,那就意味着它们被阻塞,等待着什么。 通常这是IO。 这里的重要专栏是si等。 检查他们是否填充高值。 如果是这样,这可能是你的问题 – 有些东西消耗了大量的内存,超过了你的实际能力。 使用top(4)并按内存%(shift + m在顶部)对列进行sorting可能会显示罪魁祸首。

你的系统并不是不可能的,因为你的系统在交换和交换饱和,磁盘饱和,导致线程和进程被阻塞。工具iostat(8)sysstat包的一部分,通常)应该是一个旋风,看看你是否有进程被阻塞,卡在IO_WAIT上。 饱和的磁盘在高负载的情况下会给整个系统带来坏消息,尤其是系统交换的时候。

例如,您可以每五秒运行一次扩展统计信息的iostat:

 [0:508] callisto:cyanotype $ iostat -x 5 Linux 2.6.35-23-generic (callisto) 2010-11-30 _x86_64_ (8 CPU) avg-cpu: %user %nice %system %iowait %steal %idle 16,55 0,12 2,70 2,60 0,00 78,02 Device: rrqm/s wrqm/sr/sw/s rsec/s wsec/s avgrq-sz avgqu-sz await svctm %util sdc 0,00 2,00 1,00 0,80 27,20 22,40 27,56 0,01 3,33 3,33 0,60 sdd 0,00 12,60 67,60 4,80 4222,40 139,20 60,24 0,62 8,62 3,29 23,80 sde 0,00 0,00 0,00 0,00 0,00 0,00 0,00 0,00 0,00 0,00 0,00 sdf 0,00 0,00 0,00 0,00 0,00 0,00 0,00 0,00 0,00 0,00 0,00 avg-cpu: %user %nice %system %iowait %steal %idle 32,02 0,10 1,83 0,44 0,00 65,61 Device: rrqm/s wrqm/sr/sw/s rsec/s wsec/s avgrq-sz avgqu-sz await svctm %util sdc 0,60 3,20 11,00 0,80 265,60 32,00 25,22 0,05 3,90 2,88 3,40 sdd 0,00 8,20 0,00 3,00 0,00 89,60 29,87 0,02 8,00 7,33 2,20 sde 0,00 0,00 0,00 0,00 0,00 0,00 0,00 0,00 0,00 0,00 0,00 sdf 0,00 0,00 0,00 0,00 0,00 0,00 0,00 0,00 0,00 0,00 0,00 avg-cpu: %user %nice %system %iowait %steal %idle 49,26 0,22 3,12 0,12 0,00 47,28 Device: rrqm/s wrqm/sr/sw/s rsec/s wsec/s avgrq-sz avgqu-sz await svctm %util sdc 6,20 3,00 7,40 3,80 208,00 54,40 23,43 0,09 7,86 2,50 2,80 sdd 0,00 15,20 0,20 4,00 1,60 152,00 36,57 0,03 6,67 6,19 2,60 sde 0,00 0,00 0,00 0,00 0,00 0,00 0,00 0,00 0,00 0,00 0,00 sdf 0,00 0,00 0,00 0,00 0,00 0,00 0,00 0,00 0,00 0,00 0,00 avg-cpu: %user %nice %system %iowait %steal %idle 16,00 0,54 1,05 1,07 0,00 81,35 Device: rrqm/s wrqm/sr/sw/s rsec/s wsec/s avgrq-sz avgqu-sz await svctm %util sdc 4,20 0,00 31,40 0,00 3204,80 0,00 102,06 0,17 4,90 2,68 8,40 sdd 0,00 28,20 0,20 2,60 1,60 246,40 88,57 0,02 7,14 7,14 2,00 sde 0,00 0,00 0,00 0,00 0,00 0,00 0,00 0,00 0,00 0,00 0,00 sdf 0,00 0,00 0,00 0,00 0,00 0,00 0,00 0,00 0,00 0,00 0,00 ^C 

这应该让你很容易看到你的卷是否饱和。 比如在这里,你可以看到我的磁盘使用率非常低,系统大部分CPU周期闲置等等。如果这个百分比主要在%IOWAIT列中,那么你在这里有一个IO瓶颈。 你可能已经知道这一切,但只是覆盖所有的基地,以确保。

这个想法是,你的configuration文件改变了,你没有它的历史(把你的configuration文件在版本控制下是一个很好的主意,因为这个原因) – 并不是不可能的一个缓冲区的大小突然改变,从而使昂贵像没有select的COUNT(*)的查询suddendly开始吞噬ressources。

根据你从之前使用的工具abive学到的东西 – 你应该检查一下configuration文件(是唯一改变的东西,这很可能是罪魁祸首),看看缓冲区值是否对你的平均负载。

缓冲区有多大,比如query_cache_size值,特别是sort_buffer大小? (如果这不适合内存,它将在磁盘上执行,成本很高,我相信你可以想象)。

innodb_buffer_pool_size

table_cache多大,最重要的是,这个值是否符合文件句柄的系统限制? (在[mysqld]和操作系统级都有开放文件限制)。

另外,如果这仍然是真的,我不记得我的头顶上,但我相当肯定innodb实际上locking整个表,每当它必须提交自动增量字段。 我GOOGLE了,我不知道如果这仍然是真的。

你也可以使用innotop(1)来看看更详细的情况。

我希望这有助于某种程度或给你一个起点:)

这在innodb_file_per_tabledefault-storage-engine = innodb和创build临时表的经常访问的页面的组合中certificate是一个缺陷。 每次closures连接时,都会丢弃表, 丢弃缓冲池LRU中的页面 。 这将导致服务器停顿一下,但从来没有在实际上导致问题的查询。

更糟糕的是, innodb_file_per_table设置已经在我们的my.cnf文件中搁置了好几个月,因为服务器必须重新启动,原因完全不相关,在此期间我们一直在使用这些临时表。 (NOC突然取下了DNS服务器,导致每个新的连接挂起,因为我们还没有启用skip-name-resolve ,并且几个小时之内不会承认有任何改变。)

幸运的是,我们能够重写有问题的页面来使用更快的一组查询,这些查询将大部分工作加载到前端Web服务器上,并且从那时起就没有出现问题。