MySQL如何使用內存?
首先,介紹MySQL使用內存的一些方法:
1. 會話級別的內存消耗(連接私有內存):如sort_buffer_size等,每個會話都會開辟一個sort_buffer_size來進行排序操作。
2. 全局的內存消耗(共享內存):例如:innodb_buffer_pool_size等,全局共享的內存段。
MySQL內存計算器:http://www.mysqlcalculator.com
全局內存消耗(共享內存)相關參數
1)innodb_buffer_pool_size
使用過Innodb的同學都知道,這塊內存是Innodb存儲引擎最重要的內存,直接關系到MySQL的讀寫性能。與MyISAM表只緩存索引,數據寄望於OS系統緩存不同。Innodb一般都會關閉OS的緩存,所有讀到數據頁和索引都直接存在數據庫層的innodb_buffer_pool中的。
InnoDB緩沖池緩存着InnoDB表,索引,及其它輔助緩沖器中的數據。為了實現大容量讀取操作的效率,緩沖池被分成可以容納多行的頁。為了緩存管理的效率,緩沖池被實現為頁面的鏈接列表,很少使用的數據使用LRU算法的變體進行頁面替換。
緩沖池的大小對於系統性能很重要:
- InnoDB使用malloc()方法在服務器啟動時為整個緩沖池分配內存,通常,推薦innodb_buffer_pool_size值為系統內存的50%至75%。innodb_buffer_pool_size可以在服務器運行時動態配置。
- 在具有大量內存的系統上,你可以通過將緩沖池划分為多個緩沖池實例來提高並發性,其innodb_buffer_pool_instances系統變量用來定義緩沖池實例的數量。
- 緩沖池太小可能會導致過多的交換,因為頁面從緩沖池中刷新后僅在短時間內可能再次需要。
- 緩沖池太大可能會因為內存競爭而導致交換。
2)innodb_additional_mem_pool_size
主要用於存放MySQL內部的數據結構和Innodb的數據字典,所以大小主要與表的數量有關,表越多值越大。慶幸的是這個值是可變的,如果不夠用的話,MySQL會向操作系統申請的。該值默認8M,AWS所有規格都是統一的2M,由於這個值可以動態申請,所以我覺得2M應該是滿足需求的。
3)innodb_log_buffer_size
這個是redolog的緩沖區,為了提高性能,MySQL每次寫日志都將日志先寫到一個內存Buffer中,然后將Buffer按照innodb_flush_log_at_trx_commit的配置刷到disk上。目前,我們所有實例的innodb_flush_log_at_trx_commit設置為了1,即每次事務提交都會刷新Buffer到磁盤,保證已經提交的事務,redo是不會丟的。AWS該值也設的是1(為了保證不丟數據),這個值的大小主要影響到刷磁盤的次數,設置的過小,Buffer容易滿,就會增加fsync的次數,設置過大,占用內存。該值默認是8M,AWS所有規格統一128M,我覺得目前每次提交都會刷buffer,所以除非有大事務的情況,一般buffer不太可能被占滿,所以沒必要開的很大, 8M應該是滿足需求的。
4)key_buffer_size
MyISAM表的key緩存,這個只對MyISAM存儲引擎有效,所以對於我們絕大多數使用Innodb的應用,無需關心。
5)query_cache_size
MySQL對於查詢的結果會進行緩存來節省解析SQL、執行SQL的花銷,query_cache是按照SQL語句的Hash值進行緩存的,同時SQL語句涉及的表發生更新,該緩存就會失效,所以這個緩存對於特定的讀多更新少的庫比較有用,對於絕大多數更新較多的庫可能不是很適用,比較受限於應用場景,所以AWS也把這個緩存給關了。我覺得這個值默認應該關閉,根據需求調整。
會話級別的內存消耗(連接私有內存)
上面這些就是MySQL主要的共享內存空間,這些空間是在MySQL啟動時就分配的,但是並不是立即使用的。MySQL還有一部分內存是在用戶連接請求到達時動態分配的,即每個MySQL連接都單獨一個緩存,這部分緩存主要包括:
1)read_buffer_size
每個線程連續掃描時為掃描的每個表分配的緩存區的大小(字節)。如果進行多次連續掃描,可能還需要增加該值。默認值為1311072,只有當查詢需要的時候,才分配read_buffer_size指定的全部內存。
2)read_rnd_buffer_size
當以任意順序讀取行時,可以分配隨機讀取緩沖區,通過該緩沖區讀取行,以避免磁盤尋找。read_rnd_buffer_size系統變量決定緩沖器大小。
3)sort_buffer_size
每一個要做排序的請求,都會分到一個sort_buffer_size大的緩存,用於做order by和group by的排序,如果設置的緩存大小無法滿足需要,MySQL會將數據寫入磁盤來完成排序。因為磁盤操作和內存操作不在一個數量級,所以sort_buffer_size對排序的性能影響很大。由於這部分緩存是即使不用這么大,也會全部分配的,所以對系統內存分配開銷是比較大的,如果是希望擴大的話,建議在會話層設置,默認值2M,AWS也是2M。
4)thread_stack
默認256K,AWS設置為256K,MySQL為每個線程分配的堆棧大小,當線程堆棧太小時,這限制了服務器可以處理的SQL語句的復雜性。
5)join_buffer_size
每個連接的每次join都分配一個,默認值128K,AWS設置為128K。
6)binlog_cache_size
類似於innodb_log_buffer_size緩存事務日志,binlog_cache_size緩存Binlog,不同的是這個是每個線程單獨一個,主要對於大事務有較大性能提升。默認32K,AWS 32K。
7)tmp_table_size
默認16M,用戶內存臨時表的最大值,如果臨時表超過該值,MySQL就會把臨時表轉換為一個磁盤上mysiam表。如果用戶需要做一些大表的groupby的操作,可能需要較大的該值,由於是與連接相關的,同樣建議在會話層設置。
MySQL 5.7 OOM問題診斷
其實導致OOM的直接原因並不復雜,就是因為服務器內存不足,內核需要回收內存,回收內存就是kill掉服務器上使用內存最多的程序,而MySQL服務是使用內存最多,所以就OOM了。今天,來談談MySQL的OOM(out of memory)問題診斷。之前,這類問題的定位對於普通用戶來說並不怎么簡單。但是在MySQL 5.7中,OOM問題的定位變得極其容易。還沒掌握的小伙伴趕快來看下吧。通常來說,發生OOM時可在系統日志找到類似的日志提示:
1
2
3
4
5
|
Mar 26 00:00:20 AY1301300558264084667 kernel: [4687241.322857] Out of memory: Kill process 6726 (mysqld) score 96 or sacrifice child
Mar 26 00:00:20 AY1301300558264084667 kernel: [4687241.322922] Killed process 6726 (mysqld) total-vm:1673084kB, anon-rss:147984kB, file-rss:0kB
Mar 26 00:00:20 AY1301300558264084667 kernel: [4687241.947815] python invoked oom-killer: gfp_mask=0x201da, order=0, oom_adj=0, oom_score_adj=0
Mar 26 00:00:20 AY1301300558264084667 kernel: [4687241.947819] python cpuset=/ mems_allowed=0
Mar 26 00:00:20 AY1301300558264084667 kernel: [4687241.947822] Pid: 25708, comm: python Not tainted 3.2.0-29-generic #46-Ubuntu
|
MySQL 5.7的庫performance_schema新增了以下這幾張表,用於從各維度查看內存的消耗:
- 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
簡單來說,就是可以根據用戶、主機、線程、賬號、全局的維度對內存進行監控。同時庫sys也就這些表做了進一步的格式化,可以使得用戶非常容易的觀察到每個對象的內存開銷:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
mysql> select event_name,current_alloc from memory_global_by_current_bytes limit 10;
+-----------------------------------------------------------------------------+---------------+
| event_name | current_alloc |
+-----------------------------------------------------------------------------+---------------+
| memory/mysys/IO_CACHE | 482.41 MiB |
| memory/performance_schema/events_statements_summary_by_thread_by_event_name | 198.38 MiB |
| memory/performance_schema/memory_summary_by_thread_by_event_name | 129.38 MiB |
| memory/performance_schema/events_statements_current | 80.41 MiB |
| memory/performance_schema/events_statements_history | 80.41 MiB |
| memory/performance_schema/events_waits_summary_by_thread_by_event_name | 74.39 MiB |
| memory/performance_schema/events_statements_current.sqltext | 57.50 MiB |
| memory/performance_schema/events_statements_current.tokens | 57.50 MiB |
| memory/performance_schema/events_statements_history.tokens | 57.50 MiB |
| memory/performance_schema/events_statements_history.sqltext | 57.50 MiB |
+-----------------------------------------------------------------------------+---------------+
10 rows in set (0.04 sec)
|
細心的同學可能會發現,默認情況下performance_schema只對performance_schema進行了內存開銷的統計。根據你的MySQL安裝代碼區域可能包括performance_schema、sql、client、innodb、myisam、csv、memory、blackhole、archive、partition和其他。
但是在對OOM進行診斷時,需要對所有可能的對象進行內存監控。因此,還需要做下面的設置:
1
2
3
|
mysql> update performance_schema.setup_instruments set enabled = 'yes' where name like 'memory%';
Query OK, 306 rows affected (0.00 sec)
Rows matched: 376 Changed: 306 Warnings: 0
|
1
2
3
4
5
6
7
8
9
10
11
|
mysql> select * from performance_schema.setup_instruments where name like 'memory%innodb%' limit 5;
+-------------------------------------------+---------+-------+
| NAME | ENABLED | TIMED |
+-------------------------------------------+---------+-------+
| memory/innodb/adaptive hash index | YES | NO |
| memory/innodb/buf_buf_pool | YES | NO |
| memory/innodb/dict_stats_bg_recalc_pool_t | YES | NO |
| memory/innodb/dict_stats_index_map_t | YES | NO |
| memory/innodb/dict_stats_n_diff_on_level | YES | NO |
+-------------------------------------------+---------+-------+
5 rows in set (0.00 sec)
|
但是這種在線打開內存統計的方法僅對之后新增的內存對象有效:
1
2
3
4
5
6
7
8
9
10
11
12
13
|
mysql> select event_name,current_alloc from memory_global_by_current_bytes where event_name like '%innodb%';
+-------------------------+---------------+
| event_name | current_alloc |
+-------------------------+---------------+
| memory/innodb/row0sel | 21.10 MiB |
| memory/innodb/btr0pcur | 89.53 KiB |
| memory/innodb/os0event | 33.73 KiB |
| memory/innodb/dict0dict | 32.05 KiB |
| memory/innodb/trx0undo | 30.94 KiB |
| memory/innodb/rem0rec | 13.35 KiB |
| memory/innodb/fil0fil | 5.57 KiB |
+-------------------------+---------------+
7 rows in set (0.04 sec)
|
如想要對全局生命周期中的對象進行內存統計,必須在配置文件中進行設置,然后重啟:
1
2
|
[mysqld]
performance-schema-instrument='memory/%=COUNTED'
|
1
2
3
4
5
6
7
8
9
10
11
|
mysql> select event_name,current_alloc from memory_global_by_current_bytes limit 5;
+-----------------------------------------------------------------------------+---------------+
| event_name | current_alloc |
+-----------------------------------------------------------------------------+---------------+
| memory/mysys/IO_CACHE | 498.47 MiB |
| memory/performance_schema/events_statements_summary_by_thread_by_event_name | 198.38 MiB |
| memory/performance_schema/memory_summary_by_thread_by_event_name | 129.38 MiB |
| memory/performance_schema/events_statements_current | 80.41 MiB |
| memory/performance_schema/events_statements_history | 80.41 MiB |
+-----------------------------------------------------------------------------+---------------+
5 rows in set (0.04 sec)
|
通過上面的結果,是不是可以發現可疑的內存使用了呢?
另外可以看看buffer pool page的使用情況。
1
2
3
4
5
6
7
8
9
10
|
mysql> show engine innodb status\G
----------------------
INDIVIDUAL BUFFER POOL INFO
----------------------
---BUFFER POOL 0
Buffer pool size 49146
Free buffers 22120
Database pages 26248
Old database pages 9669
Modified db pages 1
|
對於MySQL 5.7,可以使用sys庫下的memory_global_by_current_bytes表來查詢相同的底層數據,該模式表顯示了全局服務器內當前內存使用情況,按分配類型進行細分。
1
2
3
4
5
6
7
8
9
|
mysql> SELECT * FROM sys.memory_global_by_current_bytes WHERE event_name LIKE 'memory/innodb/buf_buf_pool'\G
*************************** 1. row ***************************
event_name: memory/innodb/buf_buf_pool
current_count: 1
current_alloc: 131.06 MiB
current_avg_alloc: 131.06 MiB
high_count: 1
high_alloc: 131.06 MiB
high_avg_alloc: 131.06 MiB
|
此sys模式查詢通過current_alloc()代碼區域聚合當前分配的內存:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
mysql> 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/innodb | 843.24 MiB |
| memory/performance_schema | 81.29 MiB |
| memory/mysys | 8.20 MiB |
| memory/sql | 2.47 MiB |
| memory/memory | 174.01 KiB |
| memory/myisam | 46.53 KiB |
| memory/blackhole | 512 bytes |
| memory/federated | 512 bytes |
| memory/csv | 512 bytes |
| memory/vio | 496 bytes |
+---------------------------+---------------+
|
完結。。。