020:InnoDB Buffer Pool
一. 緩沖池(Buffer Pool)
1. 緩沖池介紹
- 每次
讀寫
數據都是通過Buffer Pool
; - 當
Buffer Pool
中沒有用戶所需要的數據時,才去硬盤中
獲取; - 通過
innodb_buffer_pool_size
進行設置總容量,該值設置的越大越好; innodb_buffer_pool_instances 設置為
多個
緩沖池;- 總容量還是
innodb_buffer_pool_size
設置多個
instance
可將熱點打散,提高並發性能(建議設置成CPU個數值)Buffer Pool也是以
頁(page)
為單位的,且大小和innodb_page_size
一致;
2. Buffer Pool 性能測試
18G的測試數據,80M條記錄;
- 當
Buffer Pool
使用的內存超過
數據庫的大小 時,比如20G(庫中所有數據都在內存中),此時的性能有了很大的提升; - 該圖測試的是`
TPS (每秒事物數)
,sysbench中一個事物由18條SQL語句組成,即這裏的QPS為4.5W 內存減少 10% ,性能下降 60%
3. Buffer Pool的管理
3.1 Buffer Pool 的組成
1.Free List
- Buffer Pool 剛啟動時,有一個個16K的空白的頁,這些頁就存放(鏈表串聯)在 Free List 中
2.LRU List
- 當讀取一個數據頁的時候,就從 Free List 中取出一個頁,存入數據,並將該頁放入到 LRU List 中
3.Flush List
- 當 LRU List 中的頁 第一次 被修改了,就將該頁的 指針(page number) 放入了 Flush List (只要修改過,就放入,不管修改幾次)
- Flush List 中包含臟頁(數據經過修改,但是未刷入磁盤的頁)
- Flush list 中存放的不是一個頁,而是頁的指針(page number)
3.2 查看Buffer Pool的狀態
- 使用命令 show engine innodb status\G 配合 pager less
mysql gcdb@localhost:(none)> show engine innodb status;
+--------+------+----------------------------------------------------------------------------------+
| Type | Name | Status |
+--------+------+----------------------------------------------------------------------------------+
| InnoDB | | |
| | | ===================================== |
| | | 2018-01-05 14:18:27 0x7fa8b4649700 INNODB MONITOR OUTPUT |
| | | ===================================== |
-- ---------------省略其他輸出-----------------
| | | ---------------------- |
| | | BUFFER POOL AND MEMORY |
| | | ---------------------- |
| | | Total large memory allocated 10994319360 |
| | | Dictionary memory allocated 14685357 |
| | | Buffer pool size 655280 |
| | | Free buffers 648346 |
| | | Database pages 6904 |
| | | Old database pages 2662 |
| | | Modified db pages 0 |
| | | Pending reads 0 |
| | | Pending writes: LRU 0, flush list 0, single page 0 |
| | | Pages made young 2, not young 0 |
| | | 0.00 youngs/s, 0.00 non-youngs/s |
| | | Pages read 6663, created 241, written 988 |
| | | 0.00 reads/s, 0.00 creates/s, 0.39 writes/s |
| | | Buffer pool hit rate 1000 / 1000, young-making rate 0 / 1000 not 0 / 1000 |
| | | Pages read ahead 0.00/s, evicted without access 0.00/s, Random read ahead 0.00/s |
| | | LRU len: 6904, unzip_LRU len: 0 |
| | | I/O sum[0]:cur[0], unzip sum[0]:cur[0] |
--在輸出的結果中可以定位到BUFFER POOL AND MEMORY ,以下部分顯示了緩沖地的狀態以及占用內存的情況:
? Total large memory allocated 10994319360
lnnoDB 所分配的內存總量為10994319360 字節。
? Dictionary memory allocated 14685357
數據字典內存區使用 14685357字節
? Buffer pool size 16384
緩沖地中頁的數量,這裏是 655280 個頁,總共占用10G 內存( 16K*655280) 。
? Free buffers 648346
緩沖地中free 鏈表中空閑頁的數量。
? Database pages 41
Buffer Pool中使用了多少頁(LRU List)
? Old database pages 2662
最近不被訪問的數據
? Modified db pages
贓頁的頁數
? Pending reads 0
正在讀取的頁的數量,這裏為0 。
? Pending writes: LRU 0, flush list 0, single page 0
正在進行刷新頁的數量,刷新可以分為LRU 、flush list 、single page 三種類型,這在後面的
? Pages made young 2, not young 0
0.00 youngs/s, 0.00 non-youngs/s
-- young表示old-->new的狀態
? Pages read 6663, created 241, written 988
緩沖地中已經讀取、創建和刷新頁的次數,這裏分別為6663 、241 、998 。
? 0.00 reads/s, 0.00 creates/s, 0.39 writes/s
過去一段時間內,每秒頁的讀取、也IJ 建和刷新的次數。註意: SHOW INNODB STATUS 輸出
的是過去一段時間內的結果。
? Buffer pool hit rate 1000 / 1000
緩沖地的命中率,這是監控最為關註的一個性能指標。命中率越高,數據庫的性能越好,這裏為1 00% ,表示緩沖地能夠緩存所有InnoDB 存儲引擎表。
| | | ---------------------- |
| | | INDIVIDUAL BUFFER POOL INFO |
| | | ---------------------- |
| | | ---BUFFER POOL 0 |
| | | Buffer pool size 81910 |
| | | Free buffers 80996 |
| | | Database pages 910 |
| | | Old database pages 339 |
| | | Modified db pages 0 |
| | | Pending reads 0 |
| | | Pending writes: LRU 0, flush list 0, single page 0 |
| | | Pages made young 0, not young 0 |
| | | 0.00 youngs/s, 0.00 non-youngs/s |
| | | Pages read 865, created 45, written 104 |
| | | 0.00 reads/s, 0.00 creates/s, 0.00 writes/s |
| | | Buffer pool hit rate 1000 / 1000, young-making rate 0 / 1000 not 0 / 1000 |
| | | Pages read ahead 0.00/s, evicted without access 0.00/s, Random read ahead 0.00/s |
| | | LRU len: 910, unzip_LRU len: 0 |
| | | I/O sum[0]:cur[0], unzip sum[0]:cur[0] |
--同上
-- ---------------省略其他輸出-----------------
| | | ---------------------------- |
| | | END OF INNODB MONITOR OUTPUT |
| | | ============================ |
| | | |
+--------+------+----------------------------------------------------------------------------------+
(END)
- 使用元數據表 information_schema.INNODB_BUFFER_POOL_STATS
mysql gcdb@localhost:(none)> select * from information_schema.INNODB_BUFFER_POOL_STATS\G
***************************[ 1. row ]***************************
POOL_ID | 0
POOL_SIZE | 81910
FREE_BUFFERS | 80996
DATABASE_PAGES | 910
OLD_DATABASE_PAGES | 339
MODIFIED_DATABASE_PAGES | 0
PENDING_DECOMPRESS | 0
PENDING_READS | 0
PENDING_FLUSH_LRU | 0
PENDING_FLUSH_LIST | 0
PAGES_MADE_YOUNG | 0
PAGES_NOT_MADE_YOUNG | 0
PAGES_MADE_YOUNG_RATE | 0.0
PAGES_MADE_NOT_YOUNG_RATE | 0.0
NUMBER_PAGES_READ | 865
NUMBER_PAGES_CREATED | 45
NUMBER_PAGES_WRITTEN | 104
PAGES_READ_RATE | 0.0
PAGES_CREATE_RATE | 0.0
PAGES_WRITTEN_RATE | 0.0
NUMBER_PAGES_GET | 69856
HIT_RATE | 0
YOUNG_MAKE_PER_THOUSAND_GETS | 0
NOT_YOUNG_MAKE_PER_THOUSAND_GETS | 0
NUMBER_PAGES_READ_AHEAD | 768
NUMBER_READ_AHEAD_EVICTED | 0
READ_AHEAD_RATE | 0.0
READ_AHEAD_EVICTED_RATE | 0.0
LRU_IO_TOTAL | 0
LRU_IO_CURRENT | 0
UNCOMPRESS_TOTAL | 0
UNCOMPRESS_CURRENT | 0
***************************[ 2. row ]***************************
POOL_ID | 1
POOL_SIZE | 81910
FREE_BUFFERS | 80843
DATABASE_PAGES | 1063
OLD_DATABASE_PAGES | 400
MODIFIED_DATABASE_PAGES | 0
PENDING_DECOMPRESS | 0
PENDING_READS | 0
-- ---------------省略其他輸出-----------------
mysql gcdb@localhost:(none)> select * from information_schema.INNODB_BUFFER_PAGE_LRU limit 1\G
***************************[ 1. row ]***************************
POOL_ID | 0
LRU_POSITION | 0
SPACE | 0 -- space id 表空間號
PAGE_NUMBER | 7 -- 對應的頁號
PAGE_TYPE | SYSTEM
FLUSH_TYPE | 1
FIX_COUNT | 0
IS_HASHED | NO
NEWEST_MODIFICATION | 32993864040 -- 該頁最近一次(最新)被修改的LSN值
OLDEST_MODIFICATION | 0 -- 該頁在Buffer Pool中第一次被修改的LSN值,FLushList是根據該值進行排序的
-- 該值越小,表示該頁應該最先被刷新
ACCESS_TIME | 2688054668
TABLE_NAME | <null>
INDEX_NAME | <null>
NUMBER_RECORDS | 0
DATA_SIZE | 0
COMPRESSED_SIZE | 0
COMPRESSED | NO
IO_FIX | IO_NONE
IS_OLD | YES
FREE_PAGE_CLOCK | 0
1 row in set
Time: 0.143s
mysql gcdb@localhost:(none)>
-----------------省略其他輸出-----------------
3.3 Buffer Pool 在線調整
- 從 MySQL 5.7 開始,可以在線修改 innodb_buffer_pool_size
mysql gcdb@localhost:(none)> show variables like "%innodb_buffer_pool_size%";
+-------------------------+-------------+
| Variable_name | Value |
+-------------------------+-------------+
| innodb_buffer_pool_size | 10737418240 | -- innodb_buffer_pool_size為10G
+-------------------------+-------------+
1 row in set
Time: 0.012s
mysql gcdb@localhost:(none)> set global innodb_buffer_pool_size=16*1024*1024*1024; --調整為innodb_buffer_pool_size為16G
Query OK, 0 rows affected
Time: 0.008s
mysql gcdb@localhost:(none)> show variables like "%innodb_buffer_pool_size%";
+-------------------------+-------------+
| Variable_name | Value |
+-------------------------+-------------+
| innodb_buffer_pool_size | 17179869184 | --調整為innodb_buffer_pool_size為16G
+-------------------------+-------------+
1 row in set
Time: 0.012s
mysql gcdb@localhost:(none)> show engine innodb status;
+--------+------+----------------------------------------------------------------------------------+
| Type | Name | Status |
+--------+------+----------------------------------------------------------------------------------+
| InnoDB | | |
| | | ===================================== |
| | | 2018-01-05 15:23:16 0x7fa8b4649700 INNODB MONITOR OUTPUT |
| | | ===================================== |
-- ---------------省略其他輸出-----------------
| | | ---------------------- |
| | | BUFFER POOL AND MEMORY |
| | | ---------------------- |
| | | Total large memory allocated 17590910976 | --lnnoDB 所分配的內存總量為17590910976字節
| | | Dictionary memory allocated 14685357 |
| | | Buffer pool size 1048496 |
| | | Free buffers 1041477 |
| | | Database pages 7019 |
| | | Old database pages 2662 |
| | | Modified db pages 0 |
| | | Pending reads 0 |
| | | Pending writes: LRU 0, flush list 0, single page 0 |
| | | Pages made young 2, not young 0 |
| | | 0.00 youngs/s, 0.00 non-youngs/s |
| | | Pages read 6663, created 356, written 1158 |
| | | 0.00 reads/s, 0.00 creates/s, 0.00 writes/s |
| | | No buffer pool page gets since the last printout |
| | | Pages read ahead 0.00/s, evicted without access 0.00/s, Random read ahead 0.00/s |
| | | LRU len: 7019, unzip_LRU len: 0 |
| | | I/O sum[0]:cur[0], unzip sum[0]:cur[0] |
| | | ---------------------- |
| | | INDIVIDUAL BUFFER POOL INFO |
| | | ---------------------- |
| | | ---BUFFER POOL 0 |
| | | Buffer pool size 131062 | --BUFFER POOL 0 原先分配 81910頁變為131062
| | | Free buffers 130152 |
| | | Database pages 910 |
| | | Old database pages 339 |
| | | Modified db pages 0 |
| | | Pending reads 0 |
| | | Pending writes: LRU 0, flush list 0, single page 0 |
| | | Pages made young 0, not young 0 |
| | | 0.00 youngs/s, 0.00 non-youngs/s |
| | | Pages read 865, created 45, written 104 |
| | | 0.00 reads/s, 0.00 creates/s, 0.00 writes/s |
| | | No buffer pool page gets since the last printout |
| | | Pages read ahead 0.00/s, evicted without access 0.00/s, Random read ahead 0.00/s |
| | | LRU len: 910, unzip_LRU len: 0 |
| | | I/O sum[0]:cur[0], unzip sum[0]:cur[0] |
| | | ---BUFFER POOL 1 |
| | | Buffer pool size 131062 |
| | | Free buffers 129999 |
| | | Database pages 1063 |
| | | Old database pages 400 |
| | | Modified db pages 0 |
| | | Pending reads 0 |
mysql gcdb@localhost:(none)> set global innodb_buffer_pool_size=8*1024*1024*1024; -- 縮小,沒修改的頁被丟棄,修改的需要刷回磁盤
Query OK, 0 rows affected
Time: 0.001s
mysql gcdb@localhost:(none)> show variables like "%innodb_buffer_pool_size%";
+-------------------------+------------+
| Variable_name | Value |
+-------------------------+------------+
| innodb_buffer_pool_size | 8589934592 |
+-------------------------+------------+
1 row in set
Time: 0.012s
mysql gcdb@localhost:(none)>
- MySQL 5.7之前的版本,修改
innodb_buffer_pool_size
,需要重啟
3.4 LRU List 的管理
3.4.1使用
mid point
的LRU算法- LRU是
Least Recently Used
的縮寫,即最近最久未使用
,常用於頁面置換算法,是為虛擬頁式存儲管理服務的。 - 當該頁被第一次讀取 時,將該頁先放在
mid point
的位置(因為無法保證一定是活躍); - 取當被讀到第二次 時,才將改頁放入到
new page
的首部; innodb_old_blocks_pct
參數控制mid point
的位置,默認是37
,即 3/8 的位置
LRU 中new page和old page是在一個鏈表上的,想像成排隊,訪問多的就從mid point排到了鏈表的前面然後後冷的頁就慢慢被擠到了old page中,如果old中的數據繼續被多次訪問,還是會回到new中
1 : mid --> new
2 : mid --> old --> new
3 : mid --> old --> 刷回磁盤
4 : new --> old --> 刷回磁盤
- 當Free List中沒有空余的頁時,就需要從
old page
中最後的頁(被淘汰的頁)給取出,給新的查詢所使用 - 如果被淘汰的頁是
臟頁(page number在Flush List中)
,則需要先刷回磁盤後
,再給新的查詢
使用
mysql gcdb@localhost:(none)> show variables like "%innodb_old_blocks_pct%"
+-----------------------+-------+
| Variable_name | Value |
+-----------------------+-------+
| innodb_old_blocks_pct | 37 | --調整為innodb_buffer_pool_size為8G
+-----------------------+-------+
1 row in set
Time: 0.012s
- 避免掃描語句汙染LRU
- 當使用 select * from tablename; 時,該語句會讀取某個頁很多次(即該頁可能被讀取了兩次以上,讀取一條記錄,就需要讀一次頁 )
- innodb_old_blocks_time
mysql gcdb@localhost:(none)> show variables like"%innodb_old_blocks_time%"
+------------------------+-------+
| Variable_name | Value |
+------------------------+-------+
| innodb_old_blocks_time | 1000 | --設置為1s
+------------------------+-------+
1 row in set
Time: 0.012s
mysql gcdb@localhost:(none)>
當該頁被第一次
讀取時
,將該頁放在mid point
位置,但是隨後無論你讀多少次 ,我在這innodb_old_blocks_time
的時間內都不管(都視作只讀取了一次 ),等這個時間過去了(時間到),如果該頁還是被讀取
了,我才把這個頁放到new page
的首部。通常
select *
掃描操作不會高於1秒,一個頁很快就被掃完了。
4. Buffer Pool 的預熱
Buffer Pool預熱
在MySQL啟動後(MySQL5.6之前),Buffer Pool中頁的數據是空的,需要大量的時間才能把磁盤中的頁讀入到內存中,導致啟動後的一段時間性能很差。
使用該方法預熱,
強制掃描
,將數據刷入buffer pool
,但是不能真正將熱點數據放入buffer pool ;select count(1) from table force index(PRIMARY) ;select count(1) from table FORCE index(index name);在 MySQL 5.6 以後,可以在
停機
的時候dump
出buffer pool
的數據(space,page number),然後在啟動
的時候Load
進buffer pool
,該功能可以讓MySQL啟動時自動預熱
,無需人工幹預。
mysql gcdb@localhost:(none)> show variables like "%innodb_buffer_pool%";
+-------------------------------------+----------------+
| Variable_name | Value |
+-------------------------------------+----------------+
| innodb_buffer_pool_chunk_size | 134217728 |
| innodb_buffer_pool_dump_at_shutdown | ON | -- 在停機時dump出buffer pool中的(space,page)
| innodb_buffer_pool_dump_now | OFF | -- set 一下,表示現在就從buffer pool中dump
| innodb_buffer_pool_dump_pct | 25 | -- dump的百分比,是每個buffer pool文件,而不是整體
| innodb_buffer_pool_filename | ib_buffer_pool | -- dump出的文件的名字
| innodb_buffer_pool_instances | 8 |
| innodb_buffer_pool_load_abort | OFF |
| innodb_buffer_pool_load_at_startup | ON | -- 啟動時加載dump的文件,恢復到buffer pool中
| innodb_buffer_pool_load_now | OFF | -- set一下,表示現在加載 dump的文件
| innodb_buffer_pool_size | 8589934592 |
+-------------------------------------+----------------+
10 rows in set
Time: 0.013s
mysql gcdb@localhost:(none)>
[root@localhost-m(252) /r2/mysqldata]# head ib_buffer_pool --dump出來的文件
2,7560
2,7557
2,7552
2,7100
2,7096
2,7092
2,7090
2,7084
2,7082
2,7077
[root@localhost-m(252) /r2/mysqldata]#
1.dump的越多,啟動的越慢
2.頻繁的手工dump( set innodb_buffer_pool_dump_now = 1 ),會導致Buffer Pool中的數據越來越少,是因為設置了 innodb_buffer_pool_dump_pct
3.如果做了高可用,可以定期dump,然後將該dump的文件傳送到slave上,然後直接load( set innodb_buffer_pool_load_now = 1 )``(slave上的(Space,Page)和Master上的 大致相同 )
- load
now
和 dumpnow
都是異步
在後臺加載的,返回的速度很
--
--mysql 啟動
--
sehll> cat error.log
## ---------------省略其他輸出-----------------
2017-11-24T10:45:22.008199+08:00 0 [Note] InnoDB: Loading buffer pool(s) from /r2/mysqldata/ib_buffer_pool
## ---------------省略其他輸出-----------------
2017-11-24T10:45:25.716362+08:00 0 [Note] InnoDB: Buffer pool(s) load completed at 171124 10:45:25 --速度還是很快的
--
--mysql 停機
--
shell> cat error.log
## ---------------省略其他輸出-----------------
2017-12-29T10:31:47.844235+08:00 0 [Note] InnoDB: Dumping buffer pool(s) to /r2/mysqldata/ib_buffer_pool --dump buffer
2017-12-29T10:31:47.844597+08:00 0 [Note] InnoDB: Buffer pool(s) dump completed at 171229 10:31:47
## ---------------省略其他輸出-----------------
- 查看當前buffer pool中的數據的條數
[root@localhost-m(252) /r2/mysqldata]# wc -l ib_buffer_pool
129 ib_buffer_pool
mysql gcdb@localhost:(none)> set global innodb_buffer_pool_dump_now=1;
Query OK, 0 rows affected
Time: 0.001s
mysql gcdb@localhost:(none)> show status like 'Innodb_buffer_pool_dump_status';
+--------------------------------+--------------------------------------------------+
| Variable_name | Value |
+--------------------------------+--------------------------------------------------+
| Innodb_buffer_pool_dump_status | Buffer pool(s) dump completed at 180105 17:49:54 |
+--------------------------------+--------------------------------------------------+
1 row in set
Time: 0.011s
mysql gcdb@localhost:(none)>
-- 已經完成
[root@localhost-m(252) /r2/mysqldata]# wc -l ib_buffer_pool
1751 ib_buffer_pool --變為1751條
innodb_buffer_pool_dump_pct
該百分比(N<100)不是你當前
buffer pool
的總的數據(總頁數)
的N%
,而是你每個buffer pool實例中最近使用的頁
的N%
if there are 4 buffer pools with 100 pages each, and innodb_buffer_pool_dump_pct is set to 25, the 25 most recently used pages from each buffer pool are dumped
020:InnoDB Buffer Pool