某日淩晨收到db故障警示,上機器檢查,mysql已經被myqld守護進程重新啟動。檢查/var/log/messages和最近的dmesg發現mysql進程是被oom幹掉了。
資訊大概摘錄如下:
[13045702.638964] kthread invoked oom-killer: gfp_mask=0xd0, order=1, oomkilladj=0
[13045702.638969]
[13045702.638969] Call Trace: <ffffffff8014a331>{oom_kill_process+87}
[13045702.638977] <ffffffff8014a722>{out_of_memory+271} <ffffffff8013ce59>{autoremove_wake_function+0}
ERROR: Fatal error found, match ERROR-KEYWORD 'out_of_memory'
...
[13045702.716335] Out of Memory: Kill process 25795 (mysqld) score 1591671 and children.
ERROR: Fatal error found, match ERROR-KEYWORD 'Out of Memory'
[13045702.716359] Out of memory: Killed process 25795 (mysqld).
.....
[13045702.802080] Out of Memory: Kill process 1907 (mysqld) score 955002 and children.
ERROR: Fatal error found, match ERROR-KEYWORD 'Out of Memory'
[13045702.802102] Out of memory: Killed process 1907 (mysqld).
....
[13045762.203463] Out of Memory: Kill process 24544 (mysqld) score 341071 and children.
ERROR: Fatal error found, match ERROR-KEYWORD 'Out of Memory'
[13045762.203485] Out of memory: Killed process 24544 (mysqld).
[13045762.322333] sap1002 invoked oom-killer: gfp_mask=0x201d2, order=0, oomkilladj=0
.....
[13045762.479886] Out of Memory: Kill process 19530 (mysqldump) score 93607 and children.
ERROR: Fatal error found, match ERROR-KEYWORD 'Out of Memory'
[13045762.479907] Out of memory: Killed process 19530 (mysqldump).
由於記憶體不足,系統選擇性的選擇了耗用記憶體大的幾個進程kill掉了。先kill了mysqld,最後才把mysqldump kill掉,這樣有理由懷疑可能是因為mysqldump耗用了大量的記憶體導致的。
在這個db上,每天淩晨會使用mysqldump做單表備份並通過管道進行壓縮。這個備份任務已經部署了超過1年多了。
查看了後台採集的OS效能資料,也可以發現在mysqldump啟動直到備份結束,記憶體耗用會上升比較多,這樣可以肯定是因為mysqldump耗用大量記憶體導致此次故障發生。
check備份使用的指令碼,備份的邏輯大概如下:
1:show tables like 'xxx%' 擷取需要備份的表名
2:mysqldump --uxx -pyy --skip-opt <dbname> <tablename> >> <bakfilename>
至此問題就比較清晰了,mysqldump由於未使用-q參數導致耗用記憶體過大而導致OOM現象發生。
對mysqldump -q參數的解釋(取自man mysqldump)
? --quick, -q
This option is useful for dumping large tables. It forces mysqldump to retrieve rows for a table from the server a row at a time rather than retrieving the entire row set and buffering it in memory before writing it out.
這個選項被用來dump比較大的表。它強制mysqldump從伺服器一行一行的擷取資料而不是把擷取所有行的資料在輸出之前把它緩衝到記憶體中。
以下是一個測試,可以看到-q 參數對記憶體耗用的影響:
1:不帶-q參數
top輸出如下:
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
20010 mysql 15 0 18.0g 17g 4496 S 10 56.6 2765:51 mysqld
27518 mysql 25 0 4227m 4.1g 1048 R 100 13.1 0:33.05 mysqldump
記憶體耗用超過4G
2:帶-q參數
top輸出如下:
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
20010 mysql 16 0 18.0g 17g 4496 S 84 56.6 2766:12 mysqld
27686 mysql 25 0 11628 1380 1052 R 98 0.0 0:23.20 mysqldump
記憶體耗用很小,只有幾K
應對方案:
修改備份指令碼,加上-q參數。
到此還沒完,這個備份任務已經部署了超過1年了,為啥到最近才出現此故障呢?
對DB內的資料做了個統計,需要備份表從之前100M/day的資料量突然爆增到4G/day。知會研發跟進問題,最後發現是前端配置更改導致前端不斷重試導致。
體會:
在很多時候,故障的發生是很多因素交織在一起才發生的。碰到問題需要更深一層考慮去探尋問題發生的根本原因。
Linux下通過mysqldump備份MySQL資料庫成sql檔案
Linux中使用mysqldump對MySQL資料庫進行定時備份