1. 程式人生 > >020:InnoDB Buffer Pool

020:InnoDB Buffer Pool

ces recent created 完成 文件 memory and name out

一. 緩沖池(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的狀態

  1. 使用命令 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 、241998 。 ? 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)
  1. 使用元數據表 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)>
  1. 當該頁被第一次 讀取時,將該頁放在mid point 位置,但是隨後無論你讀多少次 ,我在這 innodb_old_blocks_time 的時間內都不管(都視作只讀取了一次 ),等這個時間過去了(時間到),如果該頁還是被讀取了,我才把這個頁放到 new page 的首部。

  2. 通常 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 以後,可以在 停機 的時候 dumpbuffer pool 的數據(space,page number),然後在 啟動 的時候 Loadbuffer 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上的 大致相同 )

  1. 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