国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

一文帶你了解MySQL數據庫InnoDB_Buffer_Pool

這篇具有很好參考價值的文章主要介紹了一文帶你了解MySQL數據庫InnoDB_Buffer_Pool。希望對大家有所幫助。如果存在錯誤或未考慮完全的地方,請大家不吝賜教,您也可以點擊"舉報違法"按鈕提交疑問。

前言

通過前邊的學習我們知道,對于使用InnoDB作為存儲引擎的表來說,不管是用于存儲用戶數據的索引(包括聚簇索引和二級索引),還是各種系統(tǒng)數據,都是以頁的形式存放在表空間中的,而所謂的表空間只不過是InnoDB對文件系統(tǒng)上一個或幾個實際文件的抽象,也就是說我們的數據說到底還是存儲在磁盤上的。但是各位也都知道,磁盤的速度慢的跟烏龜一樣,怎么能配得上“快如風,疾如電”的CPU呢?所以InnoDB存儲引擎在處理客戶端的請求時,當需要訪問某個頁的數據時,就會把完整的頁的數據全部加載到內存中,也就是說即使我們只需要訪問一個頁的一條記錄,那也需要先把整個頁的數據加載到內存中。將整個頁加載到內存中后就可以進行讀寫訪問了,在進行完讀寫訪問之后并不著急把該頁對應的內存空間釋放掉,而是將其緩存起來,這樣將來有請求再次訪問該頁面時,就可以省去磁盤IO的開銷了。

一、InnoDB架構

如圖:

一文帶你了解MySQL數據庫InnoDB_Buffer_Pool
我們可以看出,InnoDB分為了內存結構磁盤結構兩大部分,Buffer Pool是內存結構中最為重要且核心的組件,今天就來一起了解一下Buffer Pool的工作原理。我們可以看到,內存結構中不僅有Buffer Pool,還有Adaptive Hash Index、Log Buffer、Change Buffer等等組件,后面會單獨開辟的文章單獨進行講解

官檔地址:https://dev.mysql.com/doc/refman/8.0/en/innodb-buffer-pool.html
點擊此處跳轉

二、Buffer Pool

2.1 什么是緩沖池

官檔介紹: 緩沖池是InnoDB在訪問表和索引數據時緩存的主內存區(qū)域。緩沖池允許直接從內存訪問頻繁使用的數據,這加快了處理速度

從字面意思理解就是: MySQL InnoDB緩沖池,既然是緩沖池,那么它的作用就是緩存表數據與索引數據,把磁盤上的數據加載到緩沖池,避免每次訪問都進行磁盤IO,起到加速訪問的作用。

專業(yè)人士介紹: Buffer Pool是MySQL中最重要的內存組件,介于外部系統(tǒng)和存儲引擎之間的一個緩存區(qū),里面可以緩存磁盤上經常操作的真實數據,在執(zhí)行增刪改查操作時,先操作緩沖池中的數據(若緩沖池沒有數據,則從磁盤加載并緩存),然后再以一定頻率刷新到磁盤,從而減少磁盤 IO,加快處理速度。在緩沖池中不僅緩存了索引頁和數據頁,還包含了 undo 頁、插入緩存(insert page)、自適應哈希索引以及 InnoDB 的鎖信息等。
一文帶你了解MySQL數據庫InnoDB_Buffer_Pool

2.2 緩沖池大小的設置

緩沖池的配置通過變量innodb_buffer_pool_size來設置,通常它的大小占用內存60%-80%,MySQL默認是134217728字節(jié),也就是128M。

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

我們可以通過set persist命令設置緩沖池的大小

[root@mysql2 ~]# free -h
              total        used        free      shared  buff/cache   available
Mem:            15G        1.1G         12G         13M        1.4G         14G
Swap:           15G          0B         15G

15X0.7X1024X1024X1024=11274289152

mysql> set persist innodb_buffer_pool_size=11274289152;
Query OK, 0 rows affected (0.00 sec)

mysql> show variables like '%innodb_buffer_pool_size%';
+-------------------------+-------------+
| Variable_name           | Value       |
+-------------------------+-------------+
| innodb_buffer_pool_size | 11274289152 |
+-------------------------+-------------+
1 row in set (0.01 sec)

那我們如何判斷緩沖池的大小是否合理,可以通過:

  • show engine innodb status 如果Free buffers值為0,表示緩存池設置過小

  • show status like '%buffer_pool_wait%' 如果value值大于0,表示緩存池設置過小

mysql> show engine innodb status \G;
**********忽略部分**********
BUFFER POOL AND MEMORY
----------------------
Total large memory allocated 0
Total large memory allocated表示Buffer Pool向操作系統(tǒng)申請的連續(xù)內存空間大小,包括全部控制塊、緩存頁、以及碎片的字節(jié)大小

Dictionary memory allocated 1290731
Dictionary memory allocated表示數據字典信息分配的內存空間的字節(jié)大小,注意這個內存空間和Buffer Pool沒啥關系,不包括在Total memory allocated中

Buffer pool size   688067
Buffer pool size 表示該Buffer Pool可以容納多少緩存頁,注意,單位是頁!

Free buffers       680866
Free buffers表示當前Buffer Pool還有多少空閑緩存頁,也就是free鏈表中還有多少個節(jié)點

Database pages     7194
Database pages表示LRU鏈表中的頁的數量,包含young和old兩個區(qū)域的節(jié)點數量

Old database pages 2650
Old database pages表示LRU鏈表old區(qū)域的節(jié)點數量

Modified db pages  0
Modified db pages表示臟頁數量,也就是flush鏈表中節(jié)點的數量。

Pending reads      0
Pending reads表示正在等待從磁盤上加載到Buffer Pool中的頁面數量,需要注意的s當準備從磁盤中加載某個頁面時,會先為這個頁面在Buffer Pool中分配一個緩存頁以及它對應的控制塊,然后把這個控制塊添加到LRU的old區(qū)域的頭部,但是這個時候真正的磁盤頁并沒有被加載進來,Pending reads的值會跟著加1。

Pending writes: LRU 0, flush list 0, single page 0
Pending writes表示即將從LRU、flush鏈表和單個頁面刷新到磁盤中的頁面數量


Pages made young 23621, not young 178247
Pages made young表示LRU鏈表中曾經從old區(qū)域移動到y(tǒng)oung區(qū)域頭部的節(jié)點數量
not young表示在將innodb_old_blocks_time設置的值大于0時,首次訪問或者后續(xù)訪問某個處在old區(qū)域的節(jié)點時由于不符合時間間隔的限制而不能將其移動到y(tǒng)oung區(qū)域頭部時,Page made not young的值會加1。

0.00 youngs/s, 0.00 non-youngs/s
youngs/s表示每秒從old區(qū)域被移動到y(tǒng)oung區(qū)域頭部的節(jié)點數量
non-youngs/s表示每秒由于不滿足時間限制而不能從old區(qū)域移動到y(tǒng)oung區(qū)域頭部的節(jié)點數量

Pages read 7056, created 29120, written 45996
0.00 reads/s, 0.00 creates/s, 0.00 writes/s
表示讀取,創(chuàng)建,寫入了多少頁。后邊跟著讀取、創(chuàng)建、寫入的速率

Buffer pool hit rate 1000 / 1000, young-making rate 0 / 1000 not 0 / 1000
Buffer pool hit rate表示在過去某段時間,平均訪問1000次頁面,有多少次該頁面已經被緩存到Buffer Pool了
young-making rate表示在過去某段時間,平均訪問1000次頁面,有多少次訪問使頁面移動到y(tǒng)oung區(qū)域的頭部了
not (young-making rate)表示在過去某段時間,平均訪問1000次頁面,有多少次訪問沒有使頁面移動到y(tǒng)oung區(qū)域的頭部

Pages read ahead 0.00/s, evicted without access 0.00/s, Random read ahead 0.00/s
Pages read ahead表示每秒讀入的pages
evicted without access表示每秒讀出的pages
Random read ahead表示隨機讀人的pages

LRU len: 7194, unzip_LRU len: 0
LRU len表示LRU鏈表中節(jié)點的數量

I/O sum[5]:cur[0], unzip sum[0]:cur[0]
I/O sum表示最近50s讀取磁盤頁的總數
I/O cur表示現在正在讀取的磁盤頁數量
I/O unzip sum表示最近50s解壓的頁面數量
I/O unzip cur表示正在解壓的頁面數量

mysql> show status like '%buffer_pool_wait%';
+------------------------------+-------+
| Variable_name                | Value |
+------------------------------+-------+
| Innodb_buffer_pool_wait_free | 0     |
+------------------------------+-------+
1 row in set (0.00 sec)

或者通過分析InnoDB緩沖池的性能來驗證。

可以使用以下公式計算InnoDB緩沖池性能:
Performance = innodb_buffer_pool_reads / innodb_buffer_pool_read_requests * 100
innodb_buffer_pool_reads:表示InnoDB緩沖池無法滿足的請求數。需要從磁盤中讀取。
innodb_buffer_pool_read_requests:表示從內存中讀取邏輯的請求數。
例如,在我的服務器上,檢查當前InnoDB緩沖池的性能

mysql> show status like 'innodb_buffer_pool_read%';
+---------------------------------------+-------+
| Variable_name                         | Value |
+---------------------------------------+-------+
| Innodb_buffer_pool_read_ahead_rnd     | 0     |
| Innodb_buffer_pool_read_ahead         | 839   |
| Innodb_buffer_pool_read_ahead_evicted | 0     |
| Innodb_buffer_pool_read_requests      | 62567 |
| Innodb_buffer_pool_reads              | 3043  |
+---------------------------------------+-------+
5 rows in set (0.01 sec)

Innodb_buffer_pool_reads/Innodb_buffer_pool_read_requests*100 即 3043 /62567 *100=4.86

意味著InnoDB可以滿足緩沖池本身的大部分請求。從磁盤完成讀取的百分比非常小。因此無需增加innodb_buffer_pool_size值。

InnoDB buffer pool 命中率:

InnoDB buffer pool 命中率 = innodb_buffer_pool_read_requests / (innodb_buffer_pool_read_requests + innodb_buffer_pool_reads ) * 100

此值低于99%,則可以考慮增加innodb_buffer_pool_size。

InnoDB緩沖池狀態(tài)變量有哪些?

可以運行以下命令進行查看:show status like '%innodb_buffer_pool_pages%'

mysql> show status like '%innodb_buffer_pool_pages%';
+----------------------------------+--------+
| Variable_name                    | Value  |
+----------------------------------+--------+
| Innodb_buffer_pool_pages_data    | 4025   |
| Innodb_buffer_pool_pages_dirty   | 0      |
| Innodb_buffer_pool_pages_flushed | 215    |
| Innodb_buffer_pool_pages_free    | 684034 |
| Innodb_buffer_pool_pages_misc    | 69     |
| Innodb_buffer_pool_pages_total   | 688128 |
+----------------------------------+--------+
6 rows in set (0.01 sec)

說明:

  • Innodb_buffer_pool_pages_dataInnoDB緩沖池中包含數據的頁數。 該數字包括臟頁面和干凈頁面。 使用壓縮表時,報告的Innodb_buffer_pool_pages_data值可能大于Innodb_buffer_pool_pages_total。

  • Innodb_buffer_pool_pages_dirty顯示在內存中修改但尚未寫入數據文件的InnoDB緩沖池數據頁的數量(臟頁刷新)。

  • Innodb_buffer_pool_pages_flushed表示從InnoDB緩沖池中刷新臟頁的請求數。

  • Innodb_buffer_pool_pages_free顯示InnoDB緩沖池中的空閑頁面

  • Innodb_buffer_pool_pages_misc InnoDB緩沖池中的頁面數量很多,因為它們已被分配用于管理開銷,例如行鎖或自適應哈希索引。此值也可以計算為Innodb_buffer_pool_pages_total - Innodb_buffer_pool_pages_free - Innodb_buffer_pool_pages_data。

  • Innodb_buffer_pool_pages_totalInnoDB緩沖池的總大小,以page為單位。

  • innodb_buffer_pool_reads表示InnoDB緩沖池無法滿足的請求數。需要從磁盤中讀取。

  • innodb_buffer_pool_read_requests它表示從內存中邏輯讀取的請求數。

  • innodb_buffer_pool_wait_free通常,對InnoDB緩沖池的寫入發(fā)生在后臺。 當InnoDB需要讀取或創(chuàng)建頁面并且沒有可用的干凈頁面時,InnoDB首先刷新一些臟頁并等待該操作完成。 此計數器計算這些等待的實例。 如果已正確設置innodb_buffer_pool_size,則此值應該很小。如果大于0,則表示InnoDb緩沖池太小。

  • innodb_buffer_pool_write_request表示對緩沖池執(zhí)行的寫入次數。

2.3 緩沖池的管理

2.3.1 Buffer Pool的初始化

在 MySQL 啟動的時候,InnoDB 會為 Buffer Pool 申請一片連續(xù)的內存空間,然后按照默認的16KB的大小劃分出一個個的頁, Buffer Pool 中的頁就叫做緩存頁。此時這些緩存頁都是空閑的,之后隨著執(zhí)行增刪改查操作時,才會有磁盤上的數據頁被緩存到 Buffer Pool 中。

為了更好的管理這些在 Buffer Pool 中的緩存頁,InnoDB 為每一個緩存頁的最前面都創(chuàng)建了一個內存大小一樣的控制塊,里面包括緩存頁的表空間、頁號、緩存頁地址、鏈表節(jié)點等。

一文帶你了解MySQL數據庫InnoDB_Buffer_Pool每一個控制塊都對應一個緩存頁,在分配控制塊和緩存頁后,剩余的空間不夠一對控制塊和緩存頁的大小,就被稱為碎片空間

2.3.2 如何管理空閑頁

我們知道Buffer Pool 是一片連續(xù)的內存空間,當 MySQL 運行一段時間后,這片連續(xù)的內存空間中的緩存頁既有空閑的,也有被使用的,為了能夠快速找到空閑的緩存頁,可以使用鏈表結構。MySQL將空閑緩存頁的控制塊作為鏈表的節(jié)點,這個鏈表稱為 Free 鏈表(空閑鏈表),如圖

一文帶你了解MySQL數據庫InnoDB_Buffer_Pool

  • Free 鏈表上除了有控制塊,還有一個頭節(jié)點,該頭節(jié)點包含鏈表的頭節(jié)點地址,尾節(jié)點地址,以及當前鏈表中節(jié)點的數量等信息,頭節(jié)點是一塊單獨申請的內存空間(約占40字節(jié)),并不在Buffer Pool的連續(xù)內存空間里
  • Free 鏈表節(jié)點是一個一個的控制塊,而每個控制塊包含著對應緩存頁的地址,所以相當于 Free 鏈表節(jié)點都對應一個空閑的緩存頁
  • 每個控制塊塊里都有兩個指針分別是:(pre)指向上一個節(jié)點,(next)指向下一個節(jié)點;而且還有一個(clt)數據頁地址

buffer pool在尋找空閑數據頁的時候直接用free鏈表可以直接找到。只要有一頁數據空閑出來之后,直接把該數據頁的地址追加到free鏈表即可。反之每當需要從磁盤中加載一個頁到 Buffer Pool 中時,就從 Free鏈表中取一個空閑的緩存頁,并且把該緩存頁對應的控制塊的信息填上,然后把該緩存頁對應的控制塊從 Free 鏈表中移除

2.3.3 如何管理臟頁

設計 Buffer Pool 除了能提高讀性能,還能提高寫性能,也就是更新數據的時候,不需要每次都要寫入磁盤,而是將 Buffer Pool 對應的緩存頁標記為臟頁,然后再由后臺線程將臟頁寫入到磁盤。

那為了能快速知道哪些緩存頁是臟的,于是就設計出 Flush 鏈表,它跟 Free 鏈表類似的,鏈表的節(jié)點也是控制塊,區(qū)別在于 Flush 鏈表的元素都是臟頁。

一文帶你了解MySQL數據庫InnoDB_Buffer_Pool
有了 Flush 鏈表后,后臺線程就可以遍歷 Flush 鏈表,將臟頁寫入到磁盤。

2.3.4 如何提高緩存命中率?

Buffer Pool 的大小是有限的,對于一些頻繁訪問的數據我們希望可以一直留在 Buffer Pool 中,而一些很少訪問的數據希望可以在某些時機可以淘汰掉,從而保證 Buffer Pool 不會因為滿了而導致無法再緩存新的數據,同時還能保證常用數據留在 Buffer Pool 中。要實現這個,最容易想到的就是 LRU(Least recently used)算法

該算法的思路是,鏈表頭部的節(jié)點是最近使用的,而鏈表末尾的節(jié)點是最久沒被使用的。那么,當空間不夠了,就淘汰最久沒被使用的節(jié)點,從而騰出空間。

簡單的 LRU 算法的實現思路是這樣的:

  • 當訪問的頁在 Buffer Pool 里,就直接把該頁對應的 LRU 鏈表節(jié)點移動到鏈表的頭部。
  • 當訪問的頁不在 Buffer Pool里,除了要把頁放入到 LRU 鏈表的頭部,還要淘汰 LRU 鏈表末尾的節(jié)點。

LRU 的實現過程如下:

一文帶你了解MySQL數據庫InnoDB_Buffer_Pool

假如我們要訪問3號頁數據,因為3號頁在Buffer Pool 中,所以會把3號頁移動到頭部即可

一文帶你了解MySQL數據庫InnoDB_Buffer_Pool
假如我們要訪問6號頁數據,但是6號頁不在Buffer Pool 中,所以會淘汰了5號頁,然后在頭部加入6號頁數據

一文帶你了解MySQL數據庫InnoDB_Buffer_Pool

到這里我們可以知道,Buffer Pool 里有三種頁和鏈表來管理數據

  • Free Page(空閑頁),表示此頁未被使用,位于 Free 鏈表;
  • Clean Page(干凈頁),表示此頁已被使用,但是頁面未發(fā)生修改,位于LRU 鏈表。
  • Dirty Page(臟頁),表示此頁已被使用且已經被修改,其數據和磁盤上的數據已經不一致。當臟頁上的數據寫入磁盤后,內存數據和磁盤數據一致,那么該頁就變成了干凈頁。臟頁同時存在于 LRU 鏈表和 Flush 鏈表。

但是,MYSQL并沒有使用簡單的LRU算法,因為它無法解決下面問題:

  • 預讀失效
  • Buffer Pool 污染

2.3.5 預讀失效

先來說說 MySQL 的預讀機制

程序是有空間局部性的,靠近當前被訪問數據的數據,在未來很大概率會被訪問到。所以,MySQL
在加載數據頁時,會提前把它相鄰的數據頁一并加載進來,目的是為了減少磁盤 IO

但是可能這些被提前加載進來的數據頁,并沒有被訪問,相當于這個預讀是白做了,這個就是預讀失效。

如果使用簡單的 LRU 算法,就會把預讀頁放到 LRU 鏈表頭部,而當 Buffer Pool空間不夠的時候,還需要把末尾的頁淘汰掉。如果這些預讀頁如果一直不會被訪問到,就會出現一個很奇怪的問題,不會被訪問的預讀頁卻占用了 LRU 鏈表前排的位置,而末尾淘汰的頁,可能是頻繁訪問的頁,這樣就大大降低了緩存命中率。

怎么避免預讀失效帶來影響?

要避免預讀失效帶來影響,最好就是讓預讀的頁停留在 Buffer Pool 里的時間要盡可能的短,讓真正被訪問的頁才移動到 LRU 鏈表的頭部,從而保證真正被讀取的熱數據留在 Buffer Pool 里的時間盡可能長。

MySQL 是這樣做的,它改進了 LRU 算法,將 LRU 劃分了 2 個區(qū)域:old 區(qū)域young 區(qū)域

young 區(qū)域在 LRU 鏈表的前半部分,old 區(qū)域則是在后半部分,如圖

一文帶你了解MySQL數據庫InnoDB_Buffer_Pool

old 區(qū)域占整個 LRU 鏈表長度的比例可以通 innodb_old_blocks_pc 變量來設置,默認是 37

mysql> show variables like '%innodb_old_blocks_pc%';
+-----------------------+-------+
| Variable_name         | Value |
+-----------------------+-------+
| innodb_old_blocks_pct | 37    |
+-----------------------+-------+
1 row in set (0.01 sec)

代表整個 LRU 鏈表中 young 區(qū)域與 old 區(qū)域比例是 63:37,劃分這兩個區(qū)域后,預讀的頁就只需要加入到 old 區(qū)域的頭部,當頁被真正訪問的時候,才將頁插入 young 區(qū)域的頭部。如果預讀的頁一直沒有被訪問,就會從 old 區(qū)域移除,這樣就不會影響 young 區(qū)域中的熱點數據。這個變量是可以根據我們實際情況修改

mysql> set persist innodb_old_blocks_pct = 40;
Query OK, 0 rows affected (0.00 sec)

舉個例子

  • 假設有一個長度為 10 的 LRU 鏈表,其中 young 區(qū)域占比 70 %,old 區(qū)域占比 30%。如下

一文帶你了解MySQL數據庫InnoDB_Buffer_Pool

  • 假如我們有兩個11和12號頁被預讀了,這個頁號會被插入到old區(qū)域頭部,而old區(qū)域9和10號頁給淘汰,如果9和10號頁一直沒有被訪問到,那么就不會占用young區(qū)域的位置,而且會給young區(qū)域的數據更早被淘汰

一文帶你了解MySQL數據庫InnoDB_Buffer_Pool

  • 如果11號頁被預讀后,立刻被訪問了,那么就會將它插入到 young 區(qū)域的頭部,young 區(qū)域末尾的頁(7號),會被擠到 old 區(qū)域,作為 old 區(qū)域的頭部,這個過程并不會有頁被淘汰。

一文帶你了解MySQL數據庫InnoDB_Buffer_Pool

2.3.6 Buffer Pool污染

當某一個 SQL 語句掃描了大量的數據時,在 Buffer Pool 空間比較有限的情況下,可能會將 Buffer Pool 里的所有頁都替換出去,導致大量熱數據被淘汰了,等這些熱數據又被再次訪問的時候,由于緩存未命中,就會產生大量的磁盤 IO,MySQL 性能就會急劇下降,這個過程被稱為 Buffer Pool 污染。

注意, Buffer Pool 污染并不只是查詢語句查詢出了大量的數據才出現的問題,即使查詢出來的結果集很小,也會造成 Buffer Pool 污染。比如,在一個數據量非常大的表,執(zhí)行了這條語句:select * from t_user where name like '%a%'可能這個查詢出來的結果就幾條記錄,但是由于這條語句會發(fā)生索引失效,所以這個查詢過程是全表掃描的,接著會發(fā)生如下的過程:

  • 從磁盤讀到的頁加入到 LRU 鏈表的 old 區(qū)域頭部;
  • 當從頁里讀取行記錄時,也就是頁被訪問的時候,就要將該頁放到 young 區(qū)域頭部;
  • 接下來拿行記錄的 name 字段和字符串 ian 進行模糊匹配,如果符合條件,就加入到結果集里;
  • 如此往復,直到掃描完表中的所有記錄。

怎么解決出現 Buffer Pool 污染而導致緩存命中率下降的問題?

像前面這種全表掃描的查詢,很多緩沖頁其實只會被訪問一次,但是它卻只因為被訪問了一次而進入到 young 區(qū)域,從而導致熱點數據被替換了。

LRU 鏈表中 young 區(qū)域就是熱點數據,只要我們提高進入到 young 區(qū)域的門檻,就能有效地保證 young 區(qū)域里的熱點數據不會被替換掉。

MySQL 是這樣做的,進入到 young 區(qū)域條件增加了一個停留在 old 區(qū)域的時間判斷。

具體是這樣做的,在對某個處在 old 區(qū)域的緩存頁進行第一次訪問時,就在它對應的控制塊中記錄下來這個訪問時間:

  • 如果后續(xù)的訪問時間與第一次訪問的時間在某個時間間隔內,那么該緩存頁就不會被從 old 區(qū)域移動到 young 區(qū)域的頭部;
  • 如果后續(xù)的訪問時間與第一次訪問的時間不在某個時間間隔內,那么該緩存頁移動到 young 區(qū)域的頭部;

這個間隔時間是由 innodb_old_blocks_time 控制的,默認1000毫秒,也就是1秒

mysql> show variables like '%innodb_old_blocks_time%';
+------------------------+-------+
| Variable_name          | Value |
+------------------------+-------+
| innodb_old_blocks_time | 1000  |
+------------------------+-------+
1 row in set (0.01 sec)

也就說,只有同時滿足被訪問在 old 區(qū)域停留時間超過 1 秒兩個條件,才會被插入到 young 區(qū)域頭部,這樣就解決了 Buffer Pool 污染的問題 ,這個變量是可以根據我們實際情況進行修改

mysql> set persist innodb_old_blocks_time  = 2000;
Query OK, 0 rows affected (0.01 sec)

2.3.7 臟頁什么時候會被刷入磁盤?

引入了 Buffer Pool 后,當修改數據時,首先是修改 Buffer Pool 中數據所在的頁,然后將其頁設置為臟頁,但是磁盤中還是原數據。

因此,臟頁需要被刷入磁盤,保證緩存和磁盤數據一致,但是若每次修改數據都刷入磁盤,則性能會很差,因此一般都會在一定時機進行批量刷盤。

可能大家擔心,如果在臟頁還沒有來得及刷入到磁盤時,MySQL 宕機了,不就丟失數據了嗎?

這個不用擔心,InnoDB 的更新操作采用的是 Write Ahead Log 策略,即先寫日志,再寫入磁盤,通過 redo log 日志讓 MySQL 擁有了崩潰恢復能力。

下面幾種情況會觸發(fā)臟頁的刷新:

  • 當 redo log 日志滿了的情況下,會主動觸發(fā)臟頁刷新到磁盤;
  • Buffer Pool 空間不足時,需要將一部分數據頁淘汰掉,如果淘汰的是臟頁,需要先將臟頁同步到磁盤;
  • MySQL 認為空閑時,后臺線程回定期將適量的臟頁刷入到磁盤;
  • MySQL 正常關閉之前,會把所有的臟頁刷入到磁盤;

在我們開啟了慢 SQL 監(jiān)控后,如果你發(fā)現偶爾會出現一些用時稍長的 SQL,這可能是因為臟頁在刷新到磁盤時可能會給數據庫帶來性能開銷,導致數據庫操作抖動。如果間斷出現這種現象,就需要調大 Buffer Pool 空間或 redo log 日志的大小。

后臺有專門的線程每隔一段時間負責把臟頁刷新到磁盤,這樣可以不影響用戶線程處理正常的請求。主要有兩種刷新路徑:

  • 從LRU鏈表的冷數據中刷新一部分頁面到磁盤。后臺線程會定時從LRU鏈表尾部開始掃描一些頁面,掃描的頁面數量可以通過系統(tǒng)變量innodb_lru_scan_depth來指定,如果從里邊兒發(fā)現臟頁,會把它們刷新到磁盤。這種刷新頁面的方式被稱之為BUF_FLUSH_LRU

  • 從flush鏈表中刷新一部分頁面到磁盤。后臺線程也會定時從flush鏈表中刷新一部分頁面到磁盤,刷新的速率取決于當時系統(tǒng)是不是很繁忙。這種刷新頁面的方式被稱之為BUF_FLUSH_LIST。

有時候后臺線程刷新臟頁的進度比較慢,導致用戶線程在準備加載一個磁盤頁到Buffer Pool時沒有可用的緩存頁,這時就會嘗試看看LRU鏈表尾部有沒有可以直接釋放掉的未修改頁面,如果沒有的話會不得不將LRU鏈表尾部的一個臟頁同步刷新到磁盤(和磁盤交互是很慢的,這會降低處理用戶請求的速度)。這種刷新單個頁面到磁盤中的刷新方式被稱之為BUF_FLUSH_SINGLE_PAGE

當然,有時候系統(tǒng)特別繁忙時,也可能出現用戶線程批量的從flush鏈表中刷新臟頁的情況,很顯然在處理用戶請求過程中去刷新臟頁是一種嚴重降低處理速度的行為(畢竟磁盤的速度滿的要死),這屬于一種迫不得已的情況,不過這得放在后邊嘮叨redo?志的checkpoint時說了。

2.4 緩沖池的高并發(fā)

如果 InnoDB 存儲引擎只有一個 Buffer Pool,當高并發(fā)時,多個請求進來,那么為了保證數據的一致性(緩存頁、free 鏈表、flush 鏈表、lru 鏈表等多種操作),必須得給緩沖池加鎖了,每一時刻只能有一個請求獲得鎖去操作 Buffer Pool,其他請求只能排隊等待鎖釋放,那么此時 MySQL 的性能是有多么的低。

我們是可以通過修改變量innodb_buffer_pool_instances 給 MySQL 設置多個 Buffer Pool 來提升 MySQL 的并發(fā)能力。

innodb_buffer_pool_instances 是一個持久化只讀系統(tǒng)變量,需要授予persist_ro_variables_admin(啟用持久化只讀系統(tǒng)變量)和system_variables_admin(啟用修改或保留全局系統(tǒng)變量)的權限,

mysql> set persist_only innodb_buffer_pool_instances=4;
Query OK, 0 rows affected (0.01 sec)

修改完成后,我們需要重啟mysql

mysql> show variables like '%innodb_buffer_pool_instances%';
+------------------------------+-------+
| Variable_name                | Value |
+------------------------------+-------+
| innodb_buffer_pool_instances | 4     |
+------------------------------+-------+
1 row in set (0.02 sec)

每個 Buffer Pool 負責管理著自己的控制塊和緩存頁,有自己獨立一套 free 鏈表、flush 鏈表和 LRU鏈表

假設給 Buffer Pool 調整到 16 G,就是說變量innodb_buffer_pool_size 改為 17179869184,此時,MySQL 會為 Buffer Pool 申請一塊大小為16G 的連續(xù)內存,然后分成 4塊,接著將每一個 Buffer Pool 的數據都復制到對應的內存塊里,最后再清空之前的內存區(qū)域。那這是相當耗費時間的操作

為了解決上面的問題,Buffer Pool 引入一個機制:chunk 機制

  • 每個 Buffer Pool 其實是由多個 chunk 組成的。每個 chunk 的大小由參數 innodb_buffer_pool_chunk_size 控制,默認值是 128M。
mysql> show variables like '%innodb_buffer_pool_chunk_size%';
+-------------------------------+-----------+
| Variable_name                 | Value     |
+-------------------------------+-----------+
| innodb_buffer_pool_chunk_size | 134217728 |
+-------------------------------+-----------+
1 row in set (0.01 sec)

innodb_buffer_pool_chunk_size 這個變量如同innodb_buffer_pool_instances 一樣,是一個持久化只讀系統(tǒng)變量,修改完成后需要重啟MySQL

mysql> set persist_only innodb_buffer_pool_chunk_size  = 132417728;
Query OK, 0 rows affected (0.00 sec)
  • 每個 chunk 就是一系列的描述數據塊和對應的緩存頁。
  • 每個 Buffer Pool 里的所有 chunk 共享一套 free、flush、lru 鏈表。

一文帶你了解MySQL數據庫InnoDB_Buffer_Pool

得益于 chunk 機制,通過增加 Buffer Pool 的chunk個數就能避免了上面說到的問題。當擴大 Buffer Pool 內存時,不再需要全部數據進行復制和粘貼,而是在原本的基礎上進行增減內存。

下面舉個例子,介紹一下 chunk 機制下,Buffer Pool 是如何動態(tài)調整大小的:

  • 調整前 Buffer Pool 的總大小為 8G,調整后的 Buffer Pool 大小為 16 G。
  • 由于 Buffer Pool 的實例數是不可以變的,所以是每個 Buffer Pool 增加 2G 的大小,此時只要給每個 Buffer Pool 申請 (2048M/128M)個chunk就行了,但是要注意的是,新增的每個 chunk 都是連續(xù)的128M內存。

緩沖池大小必須始終等于或者是innodb_buffer_pool_chunk_size * innodb_buffer_pool_instances的倍數。如果將緩沖池大小更改為不等于或等于innodb_buffer_pool_chunk_size **innodb_buffer_pool_instances的倍數的值,
則緩沖池大小將自動調整為等于或者是innodb_buffer_pool_chunk_size * innodb_buffer_pool_instances的倍數的值

2.5 緩沖池數據預熱(了解)

我們關閉數據庫的時候,想將緩沖池中的數據保存到ib_Buffer_pool中,可以調整如下變量
innodb_buffer_pool_dump_pct:指定每個緩沖池最近使用的頁面讀取和轉儲的百分比。 范圍是1到100。默認值是25。例如,如果有4個緩沖池,每個緩沖池有100個page,并且innodb_buffer_pool_dump_pct設置為25,則dump每個緩沖池中最近使用的25個page。
innodb_buffer_pool_dump_at_shutdown:默認啟用。指定在MySQL服務器關閉時是否記錄在InnoDB緩沖池中緩存的頁面,以便在下次重新啟動時縮短預熱過程。
innodb_buffer_pool_load_at_startup:默認啟用。指定在MySQL服務器啟動時,InnoDB緩沖池通過加載之前保存的相同頁面自動預熱。 通常與innodb_buffer_pool_dump_at_shutdown結合使用

2.6 緩沖池的案例

2.6.1 大量的全表掃描

如果在業(yè)務中做了大量的全表掃描,那么你就可以將innodb_old_blocks_pct設置減小,增大innodb_old_blocks_time的時間,不讓這些無用的查詢數據進入old區(qū)域,盡量不讓緩存再young 區(qū)域的有用的數據被立即刷掉。(這也是治標的方法,大量全表掃描就要優(yōu)化sql和表索引結構了)

mysql> set persist innodb_old_blocks_time=4000;
Query OK, 0 rows affected (0.00 sec)

mysql> set persist innodb_old_blocks_pct=20;
Query OK, 0 rows affected (0.01 sec)

2.6.2 沒有大量的全表掃描

如果在業(yè)務中沒有做大量的全表掃描,那么你就可以將innodb_old_blocks_pct增大,減小innodb_old_blocks_time的時間,讓有用的查詢緩存數據盡量緩存在innodb_buffer_pool_size中,減小磁盤io,提高性能。

mysql> set persist innodb_old_blocks_time=1000;
Query OK, 0 rows affected (0.00 sec)

mysql> set persist innodb_old_blocks_pct=37;
Query OK, 0 rows affected (0.01 sec)

總結

今天理論的知識很多,下面簡單做一下總結:

  • 緩沖池(Buffer Pool)是一種常見的降低磁盤訪問的機制;
  • 緩沖池通常以頁(Page)為單位緩存數據,OS的Page大小一般為4KB,MySQL的Page大小一般為16KB;
  • Page可以分為Free Page(空閑頁)、Clean Page(干凈頁)、Dirty Page(臟頁);
  • 緩沖池中含有3個鏈表:LRU鏈表(LRU List)、Free鏈表(Free List)、Flush鏈表(Flush List),以及LRU鏈表和Flush鏈表的區(qū)別;
  • 緩沖池常見的管理算法是LRU,Memcache、OS、MySQL的InnoDB存儲引擎都使用了這種最近、最少使用原則算法(Least Rrecently Used);
  • MySQL的InnoDB存儲引擎對普通的LRU進行了優(yōu)化:
    • 將緩沖池分為New Sublist(新生代/Young)和Old Sublist(老生代/Old),入緩沖池的Page,優(yōu)先從Midpoint進入Old Sublist,Page被訪問,才進入New Sublist,以解決預讀(Read-Ahead)失效的問題。
    • Page被訪問,且在Old Sublist停留時間超過配置innodb_old_blocks_time閥值時,才進入New Sublist,以解決批量數據訪問,大量數據淘汰的問題。

至此今天的學習就到此結束了,愿您成為堅不可摧的自己~~~
?

You can’t connect the dots looking forward; you can only connect them looking backwards. So you have to trust that the dots will somehow connect in your future.You have to trust in something - your gut, destiny, life, karma, whatever. This approach has never let me down, and it has made all the difference in my life

如果我的內容對你有幫助,請 點贊評論、收藏,創(chuàng)作不易,大家的支持就是我堅持下去的動力

一文帶你了解MySQL數據庫InnoDB_Buffer_Pool文章來源地址http://www.zghlxwxcb.cn/news/detail-428767.html

到了這里,關于一文帶你了解MySQL數據庫InnoDB_Buffer_Pool的文章就介紹完了。如果您還想了解更多內容,請在右上角搜索TOY模板網以前的文章或繼續(xù)瀏覽下面的相關文章,希望大家以后多多支持TOY模板網!

本文來自互聯(lián)網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。如若轉載,請注明出處: 如若內容造成侵權/違法違規(guī)/事實不符,請點擊違法舉報進行投訴反饋,一經查實,立即刪除!

領支付寶紅包贊助服務器費用

相關文章

  • 【MySQL】數據庫中這么多數據類型你真的了解嗎?一文看懂不同數據類型有何區(qū)別

    【MySQL】數據庫中這么多數據類型你真的了解嗎?一文看懂不同數據類型有何區(qū)別

    創(chuàng)建表t1,將name列數據類型設置為要tinyint 插入屬性值:由于這里是tinyint類型,取值范圍是【-128–127】,當插入這個范圍內的數據顯示成功,范圍之外失敗 使用tinyint unsigned類型創(chuàng)建表t2 無符號類型取值范圍【0,255】,這點與C/C++一樣 2.1.1 小結 MySQL對于數據類型不合法行為采

    2024年02月13日
    瀏覽(23)
  • 【MySQL】一文帶你了解數據過濾

    【MySQL】一文帶你了解數據過濾

    ?? 博客主頁:博主鏈接 ?? 本文由 M malloc 原創(chuàng),首發(fā)于 CSDN?? ?? 學習專欄推薦:LeetCode刷題集! ?? 歡迎點贊 ?? 收藏 ?留言 ?? 如有錯誤敬請指正! ?? 未來很長,值得我們全力奔赴更美好的生活? ??大家好呀,今天是我第N次寫MySQL,也是最近才學習MySQL,也想著記錄

    2024年02月09日
    瀏覽(24)
  • 【MySQL】一文帶你了解檢索數據

    【MySQL】一文帶你了解檢索數據

    ?? 博客主頁:博主鏈接 ?? 本文由 M malloc 原創(chuàng),首發(fā)于 CSDN?? ?? 學習專欄推薦:LeetCode刷題集! ?? 歡迎點贊 ?? 收藏 ?留言 ?? 如有錯誤敬請指正! ?? 未來很長,值得我們全力奔赴更美好的生活? ------------------??分割線??------------------------- —————————

    2024年02月09日
    瀏覽(20)
  • 【MySQL】一文帶你了解過濾數據

    【MySQL】一文帶你了解過濾數據

    ?? 博客主頁:博主鏈接 ?? 本文由 M malloc 原創(chuàng),首發(fā)于 CSDN?? ?? 學習專欄推薦:LeetCode刷題集! ?? 歡迎點贊 ?? 收藏 ?留言 ?? 如有錯誤敬請指正! ?? 未來很長,值得我們全力奔赴更美好的生活? ??大家好呀,今天是我第N次寫MySQL,也是最近才學習MySQL,也想著記錄

    2024年02月09日
    瀏覽(26)
  • 一文帶你了解MySQL之InnoDB 數據頁結構

    一文帶你了解MySQL之InnoDB 數據頁結構

    前言 學完了記錄結構,我們該學數據頁的結構,前邊我們簡單的提了一下頁的概念,它是Innodb管理存儲空間的基本單位,頁的大小默認16KB,InnoDB為了不同的目的而設計了許多種不同類型的頁,比如存放表空間頭部信息的頁,存放Insert Buffer信息的頁,存放INODE信息的頁,存放

    2024年02月06日
    瀏覽(14)
  • 一文了解數據庫,數據倉庫,數據湖,數據集市,數據湖倉

    目錄 一、定義 1. 數據庫(Database) 2. 數據倉庫(Data Warehouse) 3. 數據湖(Data Lake) 4. 數據集市(Data Mart) 5. 數據湖倉(Data Lakehouse) 二、相同、異同 2.1 相同點 2.2 不同點 三、常見的工具 數據庫: 數據倉庫: 數據湖: 數據集市: 數據湖倉: 當然,以下是關于數據庫、數

    2024年01月17日
    瀏覽(25)
  • 一文了解數據庫vs數據倉庫vs數據湖

    一文了解數據庫vs數據倉庫vs數據湖

    大家好,我是KD,一名10年以上大數據架構研發(fā)經驗從業(yè)者,目前主要從事云原生大數據方向設計,擅長云原生技術、數據架構、數據平臺構建、大數據組件性能調優(yōu) 以下是本文目錄: 什么是數據庫? 為什么會有數據倉庫? 拆解幾個OLAP核心概念 大數據技術架構演進過程 什

    2024年01月23日
    瀏覽(16)
  • 一文帶你了解MySQL之InnoDB統(tǒng)計數據是如何收集的

    一文帶你了解MySQL之InnoDB統(tǒng)計數據是如何收集的

    前言 我們前邊嘮叨查詢成本的時候經常用到一些統(tǒng)計數據,比如通過 show table status 可以看到關于表的統(tǒng)計數據,通過 show index 可以看到關于索引的統(tǒng)計數據,那么這些統(tǒng)計數據是怎么來的呢?它們是以什么方式收集的呢?本章將聚焦于 InnoDB存儲引擎 的統(tǒng)計數據收集策略,看

    2024年02月06日
    瀏覽(28)
  • 如何使用JDBC操作數據庫?一文帶你吃透JDBC規(guī)范

    如何使用JDBC操作數據庫?一文帶你吃透JDBC規(guī)范

    大家好,我是橙子。最近又肝了幾個大夜,總結了 JDBC 完整版的基礎教程和實戰(zhàn)案例訓練??靵砜纯催@些 Java 基礎性的代碼你有沒有忘記? 在 Java 開發(fā)中,使用 Java 語言操作數據庫是非常重要的一部分,那么 Java 語言是如何操作數據庫的呢? 我們需要使用不同廠商的數據庫

    2024年02月03日
    瀏覽(1673)
  • 一文帶你了解MySQL之鎖

    一文帶你了解MySQL之鎖

    上一篇文章主要學習了事務并發(fā)執(zhí)行時可能帶來的各種問題,并發(fā)事務訪問相同記錄的情況我們大致可以劃分為3種: 讀-讀 情況:即并發(fā)事務相繼讀取相同的記錄,我們需要知道的是讀取操作本身不會對記錄有一毛錢影響,并不會引起什么問題,所以允許這種情況的發(fā)生。

    2024年02月08日
    瀏覽(28)

覺得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請作者喝杯咖啡吧~博客贊助

支付寶掃一掃領取紅包,優(yōu)惠每天領

二維碼1

領取紅包

二維碼2

領紅包