【20180611】MySQL OOM

關於MySQL OOM的排查思路

服務器發生內存泄露
  1. 如何確認服務器發生內存泄漏:
    • 一般執行free -m就查看內存的使用情況就可以了。假如cached和used的值相差特別大的話,安麼這個時候我們可以認爲發生了內存泄漏。(一般在CentOS6的版本上面可以這麼認爲,但是這個說法暫時還沒有一個比較可信的依據)
    • buffer和cache的區別:
      • buffer: 緩衝,爲了提高內存和硬盤之間的數據交換的速度而設計的,是即將要寫入磁盤的。
      • cache: 緩存,是爲了提高CPU和內存之間的數據交換速度而設計的,也就是平常見到的一級緩存,二級緩存,三級緩存,cache是從被磁盤中讀出來的。
    • 內存使用情況的計算方式()
      • total = used+free
      • buffer和cache是包含在used內的
      • buffer和cache可能是被分配出去,但是實際可能有部分cache或者buffer沒有被使用
      • -/+ buffer/cache 這行中used是實際使用的內存,free是實際可使用的空閒內存
[root@t-filter1 liuhuang]# free -m
             total       used       free     shared    buffers     cached
Mem:          7870       2567       5302          0        584       1083
-/+ buffers/cache:        898       6971
Swap:         2047       1262        785
[root@t-filter1 liuhuang]#
  1. 內存泄漏一些應急處理情況(特指MySQL)
    • 選擇重啓進程,徹底釋放內存歸還給OS
    • 找到代碼中導致泄漏的代碼,並加以修復
    • 升級程序版本,通常新版本會解決舊版本的問題
MySQL查看內存泄露

MySQL5.7的庫performance_schema新增了幾張視圖,可以用於各個維度查看內存的使用情況,還有就是這個幾個視圖的信息還可以在sys這個庫中查看:

root@mysqldb 11:18:  [performance_schema]> show tables like 'memory%';
+-----------------------------------------+
| Tables_in_performance_schema (memory%)  |
+-----------------------------------------+
| memory_summary_by_account_by_event_name |
| memory_summary_by_host_by_event_name    |
| memory_summary_by_thread_by_event_name  |
| memory_summary_by_user_by_event_name    |
| memory_summary_global_by_event_name     |
+-----------------------------------------+
5 rows in set (0.00 sec)

root@mysqldb 14:45:  [performance_schema]>

從表的名字大致就可以看出,就是根據主機,進程,用戶,全局等維度對內存進行監控。同時sys也針對這些表的格式做了進一步的優化,使得我們可以很方便的查看。

需要注意的一點就是部分內存維度的監控默認是關閉的,並不是所有的內存監控維度是開啓的,還需要自己手動開啓這些監控信息:

root@mysqldb 14:51:  [performance_schema]> update setup_instruments set enabled='yes' where name like 'memory%';

還有就是這些監控的信息只是會從當前使用的情況開始進行監控的,之前的內存使用情況是無法獲取得到的。

  1. 查看全局內存使用情況:
root@mysqldb 14:57:  [performance_schema]> select event_name,SUM_NUMBER_OF_BYTES_ALLOC  from     memory_summary_global_by_event_name       order by SUM_NUMBER_OF_BYTES_ALLOC desc LIMIT 10;
+------------------------------------------------------------------------------+---------------------------+
| event_name                                                                   | SUM_NUMBER_OF_BYTES_ALLOC |
+------------------------------------------------------------------------------+---------------------------+
| memory/performance_schema/events_statements_history_long                     |                  14320000 |
| memory/performance_schema/events_statements_summary_by_digest.tokens         |                  10240000 |
| memory/performance_schema/events_statements_history_long.tokens              |                  10240000 |
| memory/performance_schema/events_statements_history_long.sqltext             |                  10240000 |
| memory/performance_schema/table_handles                                      |                   9502720 |
| memory/performance_schema/events_statements_summary_by_thread_by_event_name  |                   9091072 |
| memory/performance_schema/memory_summary_by_thread_by_event_name             |                   5898240 |
| memory/performance_schema/events_statements_summary_by_digest                |                   5120000 |
| memory/performance_schema/events_statements_summary_by_host_by_event_name    |                   4545536 |
| memory/performance_schema/events_statements_summary_by_account_by_event_name |                   4545536 |
+------------------------------------------------------------------------------+---------------------------+
10 rows in set (0.01 sec)
  1. 查看進程內存使用情況:
root@mysqldb 14:58:  [performance_schema]> select event_name, SUM_NUMBER_OF_BYTES_ALLOC from     memory_summary_by_thread_by_event_name       order by SUM_NUMBER_OF_BYTES_ALLOC desc limit 20;
+----------------------------------------------+---------------------------+
| event_name                                   | SUM_NUMBER_OF_BYTES_ALLOC |
+----------------------------------------------+---------------------------+
| memory/sql/Relay_log_info::mts_coor          |                         0 |
| memory/sql/QUICK_RANGE_SELECT::alloc         |                         0 |
| memory/sql/table_mapping::m_mem_root         |                         0 |
| memory/sql/sp_head::call_mem_root            |                         0 |
| memory/sql/sp_head::execute_mem_root         |                         0 |
| memory/sql/sp_head::main_mem_root            |                         0 |
| memory/sql/THD::sp_cache                     |                         0 |
| memory/sql/Warning_info::m_warn_root         |                         0 |
| memory/sql/Protocol_local::m_rset_root       |                         0 |
| memory/sql/Prepared_statement::main_mem_root |                         0 |
| memory/sql/Prepared_statement_map            |                         0 |
| memory/sql/servers                           |                         0 |
| memory/sql/Table_triggers_list               |                         0 |
| memory/sql/gdl                               |                         0 |
| memory/sql/new_frm_mem                       |                         0 |
| memory/sql/help                              |                         0 |
| memory/sql/thd::main_mem_root                |                         0 |
| memory/sql/Delegate::memroot                 |                         0 |
| memory/sql/THD::transactions::mem_root       |                         0 |
| memory/sql/display_table_locks               |                         0 |
+----------------------------------------------+---------------------------+
20 rows in set (0.01 sec)

這個時候我們可以根據這些進程名去google或者其他的搜索引擎去獲取得到這些相關的信息,這個時候就可以比較很明確的根據具體的情況在進行優化和調整。

NUMA也有可能導致內存泄露
  1. 什麼是NUMA
    • 可以移步到:http://cenalulu.github.io/linux/numa/
    • NUMA是爲了解決SMP系統進程增長導致進程競用和橫向擴展槽糕的問題,它會將CPU平均劃分成若干個Chip(最多不超過4個),每個Chip都有自己的內存控制器和內存插槽,但是需要注意的是每個Chip的內存分配並不是均衡的,並且進程在本地的Chip訪問本地內存的速度是正常的速度,但是噹噹前的Chip的內存不足,需要訪問遠程內存(非本地的Chip)會比原本的慢。
  2. 查看NUMA的開啓情況和內存分配情況
    • 我本地的測試是隻分配了一個node的。
[root@TiDB-node2 ~]# numactl --show
policy: default
preferred node: current
physcpubind: 0 1 2 3 4 5 6 7
cpubind: 0
nodebind: 0
membind: 0
[root@TiDB-node2 ~]#

[root@TiDB-node2 ~]# numactl --hardware
available: 1 nodes (0)
node 0 cpus: 0 1 2 3 4 5 6 7
node 0 size: 8191 MB
node 0 free: 4093 MB
node distances:
node   0
  0:  10
[root@TiDB-node2 ~]#
  1. 關閉NUMA
    • 在BIOS設置層面關閉NUMA,缺點是需要重啓OS。
    • 或修改GRUB配置文件,缺點也是需要重啓OS。
    • 升級MySQL版本到5.6.27及以後,新增了一個選項innodb_numa_interleava,只需要重啓MySQL實例,無需重啓OS。
      • MySQL 5.6.27/5.7.9開始引用innodb_numa_interleave選項,但是在5.7.11的Release Notes裏提到numa對於online resize buffer pool支持不好,估計是這個原因導致在這之後又臨時禁用了,但官方文檔裏面沒有寫清楚。不過percona裏面一值有這個選項,可以放心使用。
關於MySQL內存泄露的一些BUG
  1. 同時開啓P_S和thread pool會導致內存泄露(percona 5.7.17版本)

    • 更早之前官方的5.7.13版本也有發生過這個情況
  2. 表mysql.gtid_executed的壓縮失敗導致記錄不斷的增長,內存不斷的增加導致OOM
    • MySQL 5.7.17以及之前的版本,當從庫設置super_read_only=1的時候,MySQL會認爲當前是可讀的,應該阻止所有的DML操作,因此GTID合併線程也會失敗。
    • MySQL 5.8雖然已經修復了這個問題,但是因此導入了新的bug:
The MySQL server is running with the --super-read-only option so it cannot execute this statement

要徹底解決這個問題,務必需要升級到MySQL5.7.19版本。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章