天天看點

多個buffer Pool執行個體 (3)—Buffer Pool(五十六)

前面說了lru連結清單,為了防止mysql的預讀和全表查詢重新整理pool的頻率太高,是以把lru連結清單分為young區域和old區域,但是頻繁的移動lru連結清單也影響性能,是以當在young後半部1/4區域的時候,才會移動到最前面。初始資料從磁盤重新整理到記憶體中,先是進入old區域,當超過1S之後繼續通路,則會移動到young區域。預讀分為兩種,第一種是當mysql檢測到執行語句按順序查詢超過一定值,則會吧下一個區的所有頁全部都預先重新整理到緩存頁裡,第二種就是13個頁在同一個區,這時候會吧這個區的資料全部重新整理到緩存頁。

LRU連結清單管理(2)—Buffer Pool(五十五)

重新整理髒頁到磁盤

前面說了每隔一段時間,會把修改了緩存頁的髒資料,重新整理到磁盤上,主要有兩個重新整理路徑:

從冷資料重新整理一部分到磁盤(BUF_FLUSH_LRU):背景線程會定時從lru連結清單尾部掃描一些頁面,如果發現髒資料,則會重新整理到磁盤。

從flush連結清單中重新整理一部分到磁盤(BUF_FLUSH_LIST):重新整理的速率取決于系統是不是很繁忙。

有時候重新整理髒資料到磁盤比較慢,如果在沒重新整理之前,有新的查詢過來,而free連結清單已經沒有空閑緩存頁,這時候需要去尾部吧資料釋放,如果尾部存在髒資料,這時候則會強行吧髒資料持久化到磁盤上。這種方式稱為BUF_FLUSH_SINGLE_PAGE。

當然有時候系統特别忙的時候,使用者大量通路時,也會出現使用者線程批量從flush連結清單持久化髒資料,磁盤的I/O操作慢的要死,顯然這是迫不得已的行為,後面redo日志的checkpoint時說。

多個buffer pool執行個體

上面說過,mysql伺服器啟動的時候,就會根系統申請buffer pool的記憶體空間,在多線程的情況下,各個連結清單都需要加鎖進行處理,但在buffer pool特别大,并且多線程通路量也别高的情況下,單一的buffer pool會影響處理速度。是以會吧buffer pool會分成各種小的buffer pool,這些稱為執行個體,他們都是獨立去申請記憶體空間,獨立管理的連結清單,并且在多線程通路的情況下互不影響,可以通過innodb_buffer_pool_instance的值來修改buffer pool可以建立幾個執行個體。

innodb_buffer_pool_instances = 2

表示我們需要兩個buffer_pool執行個體

那麼每個pool_instance占多少記憶體呢,其實就是我們之前的總數除一下

Innodb_buffer_pool_size / innodb_buffer_pool_instances

但因為建立多個執行個體管理他們也是有開銷的,mysql規定,當innoDB_buffer_pool_size在1G以下的時候,預設都是一個執行個體,設定多個也是無效的,是以隻有大于1G的時候才鼓勵設定多個執行個體。

Innodb_buffer_pool_chunk_size

在mysql5.7.5之前,buffer pool隻有在mysql伺服器啟動之前修改,在5.7.5之後,伺服器運作的時候也可以修改,但有個問題,每次當我們重新調整buffer pool的值時候 ,都要重新向系統申請一塊連續的記憶體空間,然後再将舊的資料拷貝到新的記憶體中(有木有類似java的數組),很顯然這是極其耗時的。

一個buffer_pool_instance有兩個chunk,因為有了這個概念,我們在伺服器運作的時候,都是以chunk來增加或者删除,而不需要申請一大片空間,然後拷貝資料。

Chunk的值可以在伺服器啟動之前通過innodb_buffer_pool_chunk_Size來設定,預設值是134217728,也就是128,需要注意的是,這個chunk_Size隻可以在伺服器啟動前指定。

(注意:為什麼在伺服器運作的時候不可以修改innodb_buffer_pool_chunk_size呢?還不是因為如果改了,前面資料存儲的大小和新的就不一樣,這樣還是得把前面的資料copy到新的chunk,這樣設定chunk的意義就不存在。)

配置buffer pool時的注意事項

innoDB_buffer_pool_size 必須是 innoDB buffer_pool_insatances * innoDB buffer_pool_chunk_size的倍數,這裡主要想保證每個chunk是相等的,而且不會浪費記憶體空間,産生碎片。

mysqld --innodb-buffer-pool-size=8G --innodb-buffer-pool-instances=16

當我們吧pool_size改成8g,instances為16,因為chunk_size預設是128m,8G符合的倍數。

mysql> show variables like 'innodb_buffer_pool_size'
+-------------------------+------------+
| Variable_name           | Value      |
+-------------------------+------------+
| innodb_buffer_pool_size | 8589934592 |
+-------------------------+------------+
1 row in set (0.00 sec)           

複制

檢視之後我們可以看到是正常的,但如果設定成9g,則會系統預設會改為10G

mysqld --innodb-buffer-pool-size=9G --innodb-buffer-pool-instances=1
mysql> show variables like 'innodb_buffer_pool_size';
+-------------------------+-------------+
| Variable_name           | Value       |
+-------------------------+-------------+
| innodb_buffer_pool_size | 10737418240 |
+-------------------------+-------------+
1 row in set (0.01 sec)           

複制

如果伺服器在啟動的時候,innodb_buffer_pool_chunk_size * innodb_buffer_pool_instances 大于innoDB_buffer_pool_size,這時候,chunk_size的值會預設改為 innodb_buffer_pool_chunk_size / innodb_buffer_pool_instances。

mysqld --innodb-buffer-pool-size=2G --innodb-buffer-pool-instances=16 --innodb-buffer-pool-chunk-size=256M

因為256m*16 = 4G >2g

mysql> show variables like 'innodb_buffer_pool_size';
+-------------------------+------------
| Variable_name           | Value      |
+------------------------+------------+
| innodb_buffer_pool_size | 2147483648 |
+-------------------------+------------+
1 row in set (0.01 sec)
mysql> show variables like 'innodb_buffer_pool_chunk_size';
+-------------------------------+-----------+
| Variable_name                 | Value     |
+-------------------------------+-----------
| innodb_buffer_pool_chunk_size | 134217728 |
+------------------------------+-----------
1 row in set (0.00           

複制

Buffer pool中存儲的其他資訊

Buffer pool除了存儲緩存頁外,還會存儲鎖資訊,自适應哈希索引等資訊,後面會詳細介紹。

檢視buffer pool的狀态資訊

mysql> SHOW ENGINE INNODB STATUS\G
 
(...省略前邊的許多狀态)
----------------------
BUFFER POOL AND MEMORY
----------------------
Total memory allocated 13218349056;
Dictionary memory allocated 4014231
Buffer pool size   786432
Free buffers       8174
Database pages     710576
Old database pages 262143
Modified db pages  124941
Pending reads 0
Pending writes: LRU 0, flush list 0, single page 0
Pages made young 6195930012, not young 78247510485
108.18 youngs/s, 226.15 non-youngs/s
Pages read 2748866728, created 29217873, written 4845680877
160.77 reads/s, 3.80 creates/s, 190.16 writes/s
Buffer pool hit rate 956 / 1000, young-making rate 30 / 1000 not 605 / 1000
Pages read ahead 0.00/s, evicted without access 0.00/s, Random read ahead 0.00/s
LRU len: 710576, unzip_LRU len: 118
I/O sum[134264]:cur[144], unzip sum[16]:cur[0]
--------------
(...省略後邊的許多狀态)
mysql>           

複制

total memory allocated :表示buffer pool向作業系統申請記憶體,包括控制塊,緩存頁,碎片。

dictionary memory allocated:為資料字典配置設定的記憶體空間,這個和記憶體空間buffer pool沒啥關系,不包含在total memory allocated。

buffer pool size:頁為機關,單表buffer pool 多少緩存頁。

free buffers:代表free 連結清單多少個節點,多少空閑緩存頁。

database pages:代表lru連結清單多少個頁數量,包含young區域和old區域。

old database pages:代表lru的old區域頁數量。

modified db pages:代表髒頁數量,flush連結清單頁的數量。

pending reads:正在等待從磁盤加載到buffer pool的頁面數。

當查詢開始,準備從磁盤加載某個資料,會先為buffer pool配置設定一個緩存頁和控制塊,然後把這個控制塊添加到old的頭部,但這時候真正的磁盤頁沒有加載進來,pending reads+1。

pending write LRU:即将從LRU連結清單重新整理到磁盤的頁面數。

pending write flush list:從flush 連結清單重新整理到磁盤的頁面數。

pendig write single page:即将以單個頁面的形式重新整理到磁盤的頁面數。

pages made young:代表曾從old節點數移動到young區域的數量。

隻有在old節點數量移動到young裡才+1,如果在young後面的1/4出移動到young區域頭部,并不會+1。

page made not young:當innoDb_old_block_time的時間設定1s,但初次通路的時間和最後通路的時間沒超過這個時間,導緻目前資料沒有移動到young裡,就會讓目前參數+1。

youngs/s:每秒從old區域移動到young區域的節點數。

non youngs/s:代表每秒不滿足從old移動到young區域的節點數。

pages read,created,written:代表讀取,建立寫入多少頁,後面跟着寫入的速率。

buffer pool hit rate:在過去時間段,平均通路1000次頁面,有多少次頁面已經被緩存在buffer pool。

young-making rate:再過去時間段,平均通路1000次頁面,有多少次沒有使頁面移動到young區域頭部。

這裡統計的不光從old區域移動到young區域頭部,也統計從young後半部1/4區域移動到young區域頭部的資料。

LRU len:代表lru鍊中的節點數。

unzip_lru:代表unzip_lru的節點數。

I/O sum:最近50s讀取頁的總數。

I/O cur:最近正在讀取的磁盤數量。

I/o unzip sum:最近50s解壓數量。

I/O unzip cur:正在解壓頁面數量。