記一次生產資料庫“意外”重啟的經歷
本文轉自 | 爪哇筆記
前言
在一個陽光明媚的下午,電腦右下角傳來一片片郵件提醒,同時伴隨著微信釘釘的震動,開啟一看,應用各種出錯,天兔告警,資料庫伺服器記憶體爆紅,MySql 資料庫例項掛掉了。
排查
先交代一下資料庫版本:
mysql> status -------------- mysql Ver 14.14 Distrib 5.7.22-22, for Linux (x86_64) using 6.2 Connection id: 59568 Current database: Current user: root@localhost SSL: Not in use Current pager: stdout Using outfile: '' Using delimiter: ; Server version: 5.7.22-22-log Percona Server (GPL), Release 22, Revision f62d93c Protocol version: 10
崩潰故障排除絕不是一項有趣的任務,特別是如果MySQL沒有報告崩潰的原因。例如,當MySQL記憶體不足時。
資料庫郵件告警提醒發來的訊息:
Type: mysql Tags: 生產主庫 Host: 172.16.1.66:3306 Level: critical Item: connect Value: down Message: mysql server down
登入 Grafana 監控皮膚,資料庫連線在哪個時間段曾有幅度的增長。
順手檢查一下之前的伺服器郵件監控告警記錄,上一個時間點,記憶體佔用率99%,這說明了資料庫連線的幅度增長,可能是壓垮伺服器的最後一根稻草。
其實導致OOM的直接原因並不複雜,就是因為伺服器記憶體不足,核心需要回收記憶體,回收記憶體就是kill掉伺服器上使用記憶體最多的程式,而MySQL服務可能就是使用記憶體最多,所以就OOM了。
Type: os Tags: 66資料庫 Host: 172.16.1.66: Level: critical Item: memory Value: 99% Message: too more memory usage
檢視系統日誌
我們帶著這個疑問來排查一下日誌:
# 檢視日誌 tail -500f /var/log/messages # 以下是 oom-killer Nov 27 14:55:48 itstyledb1 kernel: mysqld invoked oom-killer: gfp_mask=0x201da, order=0, oom_score_adj=0 Nov 27 14:55:48 itstyledb1 kernel: mysqld cpuset=/ mems_allowed=0-1 Nov 27 14:55:48 itstyledb1 kernel: CPU: 2 PID: 895 Comm: mysqld Kdump: loaded Not tainted 3.10.0-862.3.2.el7.x86_64 #1 Nov 27 14:55:48 itstyledb1 kernel: Hardware name: Huawei RH1288 V3/BC11HGSC0, BIOS 3.22 05/16/2016
小夥伴們繼續往下看:
0 pages HighMem/MovableOnly Nov 27 14:55:48 itstyledb1 kernel: 291281 pages reserved Nov 27 14:55:48 itstyledb1 kernel: [ pid ] uid tgid total_vm rss nr_ptes swapents oom_score_adj name Nov 27 14:55:48 itstyledb1 kernel: [ 468] 0 468 28271 4326 62 55 0 systemd-journal Nov 27 14:55:48 itstyledb1 kernel: [ 490] 0 490 11492 2 24 553 -1000 systemd-udevd Nov 27 14:55:48 itstyledb1 kernel: [ 787] 0 787 13877 18 27 96 -1000 auditd Nov 27 14:55:48 itstyledb1 kernel: [ 810] 81 810 14552 81 34 89 -900 dbus-daemon Nov 27 14:55:48 itstyledb1 kernel: [ 815] 0 815 55956 1 60 466 0 abrtd Nov 27 14:55:48 itstyledb1 kernel: [ 816] 0 816 55327 9 64 346 0 abrt-watch-log Nov 27 14:55:48 itstyledb1 kernel: [ 818] 0 818 121607 220 90 495 0 NetworkManager Nov 27 14:55:48 itstyledb1 kernel: [ 822] 0 822 5415 49 16 33 0 irqbalance Nov 27 14:55:48 itstyledb1 kernel: [ 823] 997 823 134634 97 60 1306 0 polkitd Nov 27 14:55:48 itstyledb1 kernel: [ 825] 0 825 6594 42 20 41 0 systemd-logind Nov 27 14:55:48 itstyledb1 kernel: [ 830] 0 830 31578 28 21 139 0 crond Nov 27 14:55:48 itstyledb1 kernel: [ 839] 0 839 27522 2 10 31 0 agetty Nov 27 14:55:48 itstyledb1 kernel: [ 1142] 0 1142 143454 114 97 2672 0 tuned Nov 27 14:55:48 itstyledb1 kernel: [ 1144] 0 1144 28203 11 59 246 -1000 sshd Nov 27 14:55:48 itstyledb1 kernel: [ 1145] 0 1145 97438 694 103 328 0 rsyslogd Nov 27 14:55:48 itstyledb1 kernel: [ 1369] 0 1369 22526 20 44 256 0 master Nov 27 14:55:48 itstyledb1 kernel: [ 1371] 89 1371 22596 32 46 251 0 qmgr Nov 27 14:55:48 itstyledb1 kernel: [ 5140] 0 5140 5102 1617 15 239 0 mysqld_exporter Nov 27 14:55:48 itstyledb1 kernel: [ 9430] 0 9430 55966 378 62 790 0 snmpd Nov 27 14:55:48 itstyledb1 kernel: [30320] 27 30320 22951376 13928375 43437 8163662 0 mysqld Nov 27 14:55:48 itstyledb1 kernel: [ 688] 89 688 22552 271 46 0 0 pickup Nov 27 14:55:48 itstyledb1 kernel: Out of memory: Kill process 30320 (mysqld) score 984 or sacrifice child Nov 27 14:55:48 itstyledb1 kernel: Killed process 30320 (mysqld) total-vm:91805504kB, anon-rss:55713500kB, file-rss:0kB, shmem-rss:0kB Nov 27 14:56:00 itstyledb1 systemd: mysqld.service: main process exited, code=killed, status=9/KILL Nov 27 14:56:00 itstyledb1 systemd: Unit mysqld.service entered failed state. Nov 27 14:56:00 itstyledb1 systemd: mysqld.service failed. Nov 27 14:56:00 itstyledb1 systemd: mysqld.service holdoff time over, scheduling restart. Nov 27 14:56:01 itstyledb1 systemd: Starting MySQL Server...
當out of memory發生時,outofmemory函式會選擇一個核心認為犯有分配過多記憶體 “罪行”的程式,並殺死該程式。顯然 Mysql 就是哪個“罪人”。
隨後 MySql 會自動重啟。重啟以後,記憶體是下來了,但是臨近下班的時候,差不多又又又佔滿了。
[root@itstyledb1 ~]# free -m total used free shared buff/cache available Mem: 55803 54976 241 10 585 349 Swap: 32064 25036 7028
找到MySql程式,執行以下top -p pid,記憶體使用52.4g
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND 935 mysql 20 0 79.7g 52.4g 7336 S 0.3 96.1 255:44.76 mysqld
計算記憶體使用
1)檢視MySQL全域性佔用多少記憶體
SELECT (@@innodb_buffer_pool_size +@@innodb_log_buffer_size +@@key_buffer_size) / 1024 /1024 AS MEMORY_MB;
查詢結果為:
+----------------+ | MEMORY_MB | +----------------+ | 20512.00000000 | +----------------+
2)檢視performance_schema佔用多少記憶體
SELECT SUBSTRING_INDEX(event_name,'/',2) AS code_area, sys.format_bytes(SUM(current_alloc)) AS current_alloc FROM sys.x$memory_global_by_current_bytes GROUP BY SUBSTRING_INDEX(event_name,'/',2) ORDER BY SUM(current_alloc) DESC;
查詢結果為:
+---------------------------+---------------+ | code_area | current_alloc | +---------------------------+---------------+ | memory/performance_schema | 349.80 MiB | +---------------------------+---------------+
3)檢視每個執行緒佔用多少記憶體
SELECT ( ( @@read_buffer_size + @@read_rnd_buffer_size + @@sort_buffer_size + @@join_buffer_size + @@binlog_cache_size + @@thread_stack + @@max_allowed_packet + @@net_buffer_length ) ) / (1024*1024) AS MEMORY_MB;
查詢結果為:
+-----------+ | MEMORY_MB | +-----------+ | 87.5156 | +-----------+
檢視當前執行緒
show full processlist
最終結果為:
+-----------+ | MEMORY_MB | +-----------+ | 87.5156*37| +-----------+
4)檢視 memory 儲存引擎佔用多少記憶體
SELECT SUM(max_data_length)/1024/1024 AS MEMORY_MB FROM information_schema.tables WHERE ENGINE='memory';
查詢結果為:
+---------------+ | MEMORY_MB | +---------------+ | 3857.37713909 | +---------------+
以上四項加起來差不多也就27975MB,差不錯28G的樣子,但是 MySql 程式顯示佔用了52.4G,那麼剩下24.4G去哪了?
執行緒池
此執行緒池非彼連線池,其實兩者是有很大區別的,連線池一般在客戶端設定,而執行緒池是在DB伺服器上配置;另外連線池可以取到避免了連線頻繁建立和銷燬,但是無法取到控制MySQL活動執行緒數的目標,在高併發場景下,無法取到保護DB的作用。比較好的方式是將連線池和執行緒池結合起來使用。
關於執行緒池的一些引數:
mysql> show variables like 'thread%'; +-------------------------------+---------------------------+ | Variable_name | Value | +-------------------------------+---------------------------+ | thread_handling | one-thread-per-connection | | thread_pool_high_prio_mode | transactions | | thread_pool_high_prio_tickets | 4294967295 | | thread_pool_idle_timeout | 60 | | thread_pool_max_threads | 100000 | | thread_pool_oversubscribe | 3 | | thread_pool_size | 12 | | thread_pool_stall_limit | 500 | +-------------------------------+---------------------------+
thread_handling:
該引數是配置執行緒模型,預設情況是one-thread-per-connection,也就是不啟用執行緒池。將該引數設定為pool-of-threads即啟用了執行緒池。
threadpoolsize:
該引數是設定執行緒池的Group的數量,預設為系統CPU的個數,充分利用CPU資源。
threadpooloversubscribe:
該引數設定group中的最大執行緒數,每個group的最大執行緒數為threadpooloversubscribe+1,注意listener執行緒不包含在內。
threadpoolhighpriomode:
高優先順序佇列的控制引數,有三個值(transactions/statements/none),預設是transactions,三個值的含義如下:
transactions:對於已經啟動事務的語句放到高優先順序佇列中,不過還取決於後面的threadpoolhighpriotickets引數
statements:這個模式所有的語句都會放到高優先順序佇列中,不會使用到低優先順序佇列
none:這個模式不使用高優先順序佇列
threadpoolhighpriotickets:
該引數控制每個連線最多語序多少次被放入高優先順序佇列中,預設為4294967295,注意這個引數只有在threadpoolhighpriomode為transactions的時候才有效果。
threadpoolidle_timeout:
worker執行緒最大空閒時間,預設為60秒,超過限制後會退出。
threadpoolmax_threads:
該引數用來限制執行緒池最大的執行緒數,超過該限制後將無法再建立更多的執行緒,預設為100000。
threadpoolstall_limit:
該引數設定timer執行緒的檢測group是否異常的時間間隔,預設為500ms。
最終配置如下:
#thread pool thread_handling=pool-of-threads #Group的數量,預設為系統CPU的個數,充分利用CPU資源 thread_pool_size=24 #每個group的最大執行緒數為thread_pool_oversubscribe+1 thread_pool_oversubscribe=3 performance_schema=off #extra connection,防止執行緒池滿的情況下無法登入MySQL extra_max_connections = 8 extra_port = 33333
備註:執行緒池在Percona,MariaDB,Oracle MySQL企業版中提供,Oracle MySQL社群版並不提供。
執行緒池貌似並不會直接導致記憶體不回收,網上有說同時開啟Thread pool和PS會出現記憶體洩露,但是 目前Percona server 5.7.21-20+版本已經修復了這個問題,顯然是不存在的。
慢查詢
由於是生產環境,這個問題拖得時間有點長,那麼慢查詢會不會影響記憶體使用問題呢?帶著這個問題,檢視了慢查詢後臺列表,在資料庫奔潰的前一個時間段,的確有不少慢查詢語句。但是這並不能在一定程度上說明問題,由於伺服器的 MySql 服務在殺死之前,記憶體已經見底,此時連線數並不多,也就三四十來個左右,大多處於休眠狀態,並且此時已經佔用了大部分的Swap空間。也就是說,在資源有限的情況下必定會出現不少慢查詢語句。
小結
其實這個"意外"一點也不意外,其實已經發生了多次了。但是還是做個小結吧,因為最終沒有確認問題出現在哪裡,所以還是釋出了吧,萬一有專業的DBA遇到類似的問題還可以小小的解惑一下。
參考
https://dev.mysql.com/doc/refman/5.7/en/memory-use.html
https://www.percona.com/blog/2018/06/28/what-to-do-when-mysql-runs-out-of-memory-troubleshooting-guide/
https://dev.mysql.com/doc/refman/5.7/en/thread-pool-tuning.html
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/31545814/viewspace-2284485/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- 一次生產事故的優化經歷優化
- 一次資料庫的優化經歷資料庫優化
- 重啟資料庫的一場鬧劇資料庫
- 記一次 Mac 意外重啟導致的 Homestead 問題Mac
- 一次無法mount資料庫的經歷資料庫
- 一次資料庫洩露的解決經歷資料庫
- 一則資料庫無法重啟的案例分析資料庫
- 記一次nodejs+mongodb資料庫專案學習經歷NodeJSMongoDB資料庫
- RAC資料庫重啟案例資料庫
- 記一次資料庫重啟後歸檔急劇增加的問題資料庫
- 關於Oracle中重啟資料庫的一個bugOracle資料庫
- Oracle DB 因宿主機(Linux 虛擬機器)意外關停,重啟資料庫步驟OracleLinux虛擬機資料庫
- 伺服器意外斷電導致無法重啟資料恢復伺服器資料恢復
- NOLOG方式重啟資料庫資料庫
- 記一次從刪庫到恢復的經歷
- 南行取經記(一)重啟Windows Mobile裝置Windows
- 一次關於解決資料庫偵聽的小經歷資料庫
- 記一次生產頻繁發生FullGC問題GC
- 一次生產的 JVM 優化案例JVM優化
- 監視資料庫映象的歷史記錄資料庫
- 資料庫工程師成長經歷有感資料庫工程師
- 記一次生產慢sql索引最佳化及思考SQL索引
- 通過重啟資料庫,完成了一次優化資料庫優化
- 快速掌握重啟Oracle資料庫的操作步驟Oracle資料庫
- 資料庫學習筆記1(資料管理歷史)資料庫筆記
- 用impdp匯入資料的一次經歷
- 一次生產環境的docker MySQL故障DockerMySql
- 記一次面試經歷面試
- 一次生產環境OOM排查OOM
- 記憶體資料庫發展歷程記憶體資料庫
- 記一次生產者消費者讀取 200w 資料, 寫入 2000w 資料的過程
- 資料產品經理工作的一天......
- Oracle資料庫意外刪除資料檔案的恢復(轉載)Oracle資料庫
- 記錄一次微信分享的經歷
- 記一次編譯GCC的經歷編譯GC
- 記一次 Google 面試經歷Go面試
- 記錄一次WhatTheFuck經歷
- 資料觀:從經濟大資料看中國房地產歷史走勢分析大資料