Mysql

為什麼 MySQL 查詢在“正在發送數據”狀態下堆積?

  • October 19, 2020

我們使用 InnoDB 表作為 Web 應用程序的後端,大約兩年前一切都很好,直到幾週前我們不得不重新啟動 MySQL。(我們沒有禁用反向 DNS 查找,儘管我們並沒有真正使用它們,但我們的託管系統突然停止響應這些請求。它們現在被禁用了。)不幸的是,配置文件已更改,我們沒有t 有其原始狀態的副本以供比較。

在解決了最重要的問題之後,我們遇到了一個真正的難題:在高負載下,數據庫查詢開始比平時花費更長的時間。在這種情況下,我們的七台 apache 伺服器有數百個打開的連接。執行 SHOW PROCESSLIST 顯示這些連接中有一半或更多處於“發送數據”狀態,通常為幾百秒。他們幾乎所有的查詢都是 SELECT,類似的查詢往往會聚集在一起。事實上,列表中最低的塊往往是完全相同的查詢(我希望它在查詢記憶體中),返回 1104 行,每行兩個整數。其他常見的違規者是幾百個單整數行、幾個單整數行,甚至是單個 COUNT(*) 結果的列表。

我們嘗試在其中一個期間關閉 Web 伺服器,但在重新啟動它們後一分鐘內問題又出現了。但是,完全重新啟動 mysqld 直到第二天才解決問題。問題可能是什麼,我們如何驗證和/或修復它?

事實證明,這是 、 和創建臨時表的頻繁訪問頁面的組合中的innodb_file_per_table缺陷default-storage-engine = innodb。每次連接關閉時,它都會刪除表,從緩衝池 LRU 中丟棄頁面。這會導致伺服器停頓一段時間,但絕不會出現在實際導致問題的查詢上。

更糟糕的是,該innodb_file_per_table設置在我們的文件中已經擱置了my.cnf幾個月,然後由於一個完全不相關的原因不得不重新啟動伺服器,在此期間我們一直在使用這些臨時表而沒有問題。(NOC 突然關閉了 DNS 伺服器,導致每個新連接都掛起,因為我們沒有啟用skip-name-resolve,並且在幾個小時內都不會承認任何事情發生了變化。)

幸運的是,我們能夠重寫有問題的頁面,以使用更快的查詢集,將大部分工作載入到前端 Web 伺服器上,此後再也沒有出現問題。

好吧,請注意,如果我沒記錯的話(我做數據庫工作已經有一段時間了)在 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

在這裡,我們有一個健康的系統(它是我的工作站)。第二列不包括緩衝區和記憶體,所以我實際上使用了 2177mb 的記憶體,並且有 1784 MB 可用。

最後一行顯示到目前為止我根本沒有使用交換。

然後給vmstat(8), 看看你的系統是否像瘋了一樣垃圾也會很有用。

[0:505] callisto:cyanotype $ vmstat 5 10
procs -----------memory---------- ---swap-- -----io---- -system-- ----cpu----
r  b   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”列中看到大量程序花費時間,則表示它們已被阻止,正在等待某些東西。通常是 IO。這裡的重要列是siso。檢查它們是否填充了高值。如果是這樣,這可能是您的問題 - 某些東西正在消耗大量記憶體,超出您的實際承受能力。按記憶體百分比使用top(4)和排序列(在頂部時 shift+m​​)可能會顯示罪魁禍首。

您的系統在交換和交換之間進行垃圾處理並使磁碟飽和,從而導致執行緒和程序阻塞並非不可能。該工具iostat(8)(通常是 package 的一部分sysstat)應該試一試,以查看您是否有程序被阻塞、卡住IO_等待。在高負載下,飽和磁碟可能會給整個系統帶來壞消息,尤其是在系統進行大量交換的情況下。

您可以每五秒執行一次帶有擴展統計資訊的 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/s     r/s     w/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/s     r/s     w/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/s     r/s     w/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/s     r/s     w/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 瓶頸。您可能已經知道這一切,但只是覆蓋所有基礎以確保。

這個想法是您的配置文件已更改,並且您沒有它的歷史記錄(出於這個原因,將您的配置文件置於版本控制之下是一個好主意) - 緩衝區的大小突然改變從而變得昂貴並非不可能像沒有 SELECT 的 COUNT(*) 這樣的查詢突然開始吞噬資源。

根據您從之前使用 abive 工具中學到的知識——您可能應該檢查配置文件(這是唯一改變的東西,很可能是罪魁禍首),看看緩衝區值是否適合您的平均負載.

緩衝區有多大,比如query_cache_size值,尤其是sort_buffer大小?(如果這不適合記憶體,它將在磁碟上執行,我相信你可以想像到巨大的成本)。

有多大innodb_buffer_pool_size

有多大table_cache,最重要的是,該值是否符合文件句柄的系統限制?(兩個打開文件限制在

$$ mysqld $$並在作業系統級別)。 此外,如果這仍然是真的,我不記得我的頭頂,但我相當肯定,當它必須送出自動增量欄位時,innodb 實際上會鎖定整個表。我用Google搜尋,我找不到這是否仍然是真的。

您也可以使用它innotop(1)來更詳細地查看正在發生的事情。

我希望這會有所幫助或為您提供一個起點:)

引用自:https://serverfault.com/questions/207557