亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

MySql?InnoDB存儲引擎之Buffer?Pool運行原理講解

 更新時間:2023年01月04日 14:09:14   作者:程序員小潘  
緩沖池是用于存儲InnoDB表,索引和其他輔助緩沖區(qū)的緩存數(shù)據(jù)的內存區(qū)域。緩沖池的大小對于系統(tǒng)性能很重要。更大的緩沖池可以減少磁盤I/O來多次訪問同一表數(shù)據(jù)。在專用數(shù)據(jù)庫服務器上,可以將緩沖池大小設置為計算機物理內存大小的百分之80

1. 前言

我們已經(jīng)知道,對于InnoDB存儲引擎而言,頁是磁盤和內存交互的基本單位。哪怕你要讀取一條記錄,InnoDB也會將整個索引頁加載到內存。哪怕你只改了1個字節(jié)的數(shù)據(jù),該索引頁就是臟頁了,整個索引頁都要刷新到磁盤。InnoDB是基于磁盤的存儲引擎,如果每次操作都去讀寫磁盤,那么性能將會受到很大的影響。而且絕大多數(shù)時候,程序讀寫的數(shù)據(jù)在磁盤上并不是連續(xù)的,這意味著需要執(zhí)行大量的隨機IO讀寫,磁盤隨機IO讀寫效率是非常低的,尤其是傳統(tǒng)的機械硬盤。

在解決這個問題之前,大家可以先想一想,為什么我們只想讀取一條記錄,而InnoDB會將整個頁的數(shù)據(jù)都加載到內存?因為根據(jù)計算機的局部性原理,程序接下來大概率會訪問與它相鄰的記錄,為了避免頻繁發(fā)起磁盤IO讀操作,InnoDB直接將整個頁都加載到內存,下次再訪問頁中的其它記錄時,就可以命中緩存了,減少磁盤IO操作。

問題解決的思路其實是一樣的,磁盤的速度雖然很慢,但是內存的速度快啊。這些被加載到內存里的索引頁,使用完畢后不要立即釋放,而是將它們先緩存下來,下次再訪問這些頁時,就可以命中緩存了,減少磁盤IO,從而提升性能。理論上,只要內存無限大,那么MySQL幾乎可以是基于內存的數(shù)據(jù)庫了。

InnoDB緩存索引頁的組件,就是我們今天要聊的「Buffer Pool」。

2. Buffer Pool

MySQL服務器啟動時,InnoDB會向操作系統(tǒng)申請一塊連續(xù)的內存空間用來緩存索引頁,這一塊連續(xù)的內存空間就是Buffer Pool。默認情況下Buffer Pool的大小是128MB,查看命令如下:

mysql> SHOW VARIABLES LIKE 'innodb_buffer_pool_size';
+-------------------------+-----------+
| Variable_name           | Value     |
+-------------------------+-----------+
| innodb_buffer_pool_size | 134217728 |
+-------------------------+-----------+

理論上,Buffer Pool越大,緩存的索引頁就可以更多,緩存的命中率就可以更高,對應的性能提升就越明顯。如果你的機器內存夠大,完全可以調大 Buffer Pool的大小,在配置文件里進行修改:

[server]
innodb_buffer_pool_size=2147483648
innodb_buffer_pool_instances=2

Buffer Pool最小是5MB,即使你配置的小于5MB,InnoDB也會分配5MB的內存。

innodb_buffer_pool_instances啟動項代表Buffer Pool實例的個數(shù)。是的,你沒看錯,Buffer Pool支持配置多個,不同實例之間是隔離的,互不影響。配置多個的主要原因是因為Buffer Pool由多個鏈表組成,在維護這些鏈表時需要加鎖保證同步,在高并發(fā)場景下會影響性能,配置多個實例就可以解決這個問題了。

每個Buffer Pool的大小是innodb_buffer_pool_size/innodb_buffer_pool_instances,InnoDB有規(guī)定,單個Buffer Pool實例的大小如果小于1GB,即使配置多個也不會生效。

2.1 Buffer Pool結構

Buffer Pool是用來緩存物理磁盤上的頁結構的,那它自然也是由若干個頁組成。為了與磁盤上的頁區(qū)分開,這里我們叫它「緩沖頁」。為了更好的管理這些緩沖頁,InnoDB為每個緩沖頁都創(chuàng)建了一個「控制塊」對象與之關聯(lián)。所以,Buffer Pool其實是由若干對控制塊和緩沖頁,以及一些碎片空間組成的。

為什么會有碎片空間?如果最后剩余的空間不足以分配一對控制塊和緩沖頁,就會被浪費掉,也就是碎片空間。除非你把Buffer Pool的大小設置的剛好合適。另外,控制塊的大小在正常模式下和DEBUG模式下占用的大小并不一樣,DEBUG模式下控制塊的大小約為緩沖頁的5%。

緩沖頁的結構和物理磁盤上的頁一致,也就沒什么好說的了??刂茐K主要記錄了緩沖頁所屬的表空間ID、頁號、緩沖頁在Buffer Pool中的地址、鏈表節(jié)點信息等等。我們重點關注鏈表節(jié)點,因為Buffer Pool出于不同的目的,將這些緩沖頁串聯(lián)成了多條鏈表,后面會提到。

總之,Buffer Pool的結構其實很簡單,如下圖所示:

2.2 Free鏈表

Buffer Pool是用來緩存磁盤上的頁結構的,那么第一個問題就來了。當我們要從磁盤上加載一個頁的時候,這個頁該放到Buffer Pool的哪個緩沖頁里呢?總不能遍歷整個Buffer Pool吧,哪個緩沖頁是空閑的就直接使用它,這未免也太笨拙了。

InnoDB會通過控制塊里的鏈表節(jié)點屬性,將所有空閑的緩沖頁都串聯(lián)成一條雙向鏈表,叫作「Free鏈表」。MySQL服務器剛啟動時,所有的緩沖頁都會加入到該鏈表中,因為所有的緩沖頁都沒有被使用。當我們要把磁盤上的頁加載到內存時,就從Free鏈表申請一個緩沖頁,并把它對應的控制塊從Free鏈表中移除,這比遍歷整個Buffer Pool可高效多了。

怎么找到Free鏈表呢?為了更好的管理這些鏈表,InnoDB為每條鏈表都創(chuàng)建了一個叫作「鏈表基節(jié)點」的結構,它的屬性就三個,分別記錄鏈表的頭尾節(jié)點指針、以及鏈表內的節(jié)點數(shù)量。

2.3 緩沖頁哈希表

第二個問題又來了,當我們要使用某個頁的時候,怎么知道它有沒有被加載到Buffer Pool呢?難道又要再遍歷一次所有已使用的緩沖頁嗎?未免也太笨拙了。

在同一個表空間里,每個頁都有唯一的一個頁號,所以要定位一個頁,只需要知道表空間ID+頁號就可以了。也就是說,我們完全可以建立一個哈希表,哈希表的Key就是表空間ID+頁號的組合,Value就是緩沖頁。這樣就可以快速判斷某個頁是否已經(jīng)加載到Buffer Pool了。

2.4 Flush鏈表

在執(zhí)行增刪改操作時,如果InnoDB每次都把受影響的頁同步到磁盤,那么必然會導致大量的磁盤隨機IO寫操作,這個效率是很低的。為了提升性能,InnoDB會先在內存里修改這些受影響的頁面,這些被修改過的頁面稱作「臟頁」(Dirty Page),然后由一個額外的線程負責將這些臟頁刷新到磁盤。

內存斷電數(shù)據(jù)就丟失了,那些沒來得及刷盤的臟頁豈不是數(shù)據(jù)就丟失了?不用擔心,后面聊的redo log會幫我們保證數(shù)據(jù)一致性的,這里先跳過。

第三個問題又來了,InnoDB怎么知道哪些頁是臟頁呢?再遍歷一次Buffer Pool嗎?太笨拙了,為了解決這個問題,InnoDB又引入了第二條鏈表:flush鏈表。

flush鏈表和free鏈表極其相似,也有一個鏈表基節(jié)點,當我們修改了緩沖頁里的數(shù)據(jù),InnoDB就會把該緩沖頁對應的控制塊加入到flush鏈表,等待后續(xù)的刷盤。

2.5 LRU鏈表

那些已經(jīng)被使用的緩沖頁,會從Free鏈表中移除,然后加入到一個叫作“LRU”的鏈表中。LRU是Least Recently Used的縮寫,譯為“最近最少使用”。為啥會需要LRU鏈表呢?說白了,相較于磁盤上海量的數(shù)據(jù),Buffer Pool那點內存實在是杯水車薪,當Buffer Pool中的內存不夠時,就不得不釋放掉一些頁面,來緩存新的頁面。

Buffer Pool的本質是為了減少磁盤IO的訪問,提高緩存命中率,正是因為它小才顯得極其珍貴,InnoDB更應該要用好它。如果是你,你會在Buffer Pool里放訪問頻率高的頁面,還是訪問頻率低的頁面呢?

最簡單的LRU鏈表,每當我們要訪問一個頁面時,就把它移動到LRU鏈表的表頭,那么鏈尾的頁面自然就是最近最少使用的了,當Free鏈表沒有空閑的緩沖頁時,直接把LRU鏈表的鏈尾頁面釋放掉即可??此茮]什么問題,但是某些場景下,LRU鏈表會被破壞:

1.全表掃描:全表掃描需要加載聚簇索引B+樹的所有葉子節(jié)點,當表中數(shù)據(jù)量較大時,可能一次全表掃描就會把之前訪問頻率很高的緩沖頁全部從LRU鏈表中擠出,下次再訪問這些頁面時,又得從磁盤上重新加載一遍了。

2.預讀:InnoDB內置了一個貼心的預讀功能,它會在執(zhí)行當前讀請求時,判斷是否還會訪問其它頁面,然后異步的把這些頁面提前加載到Buffer Pool,從而加速讀操作。預讀細分為兩種:

  • 2.1線性預讀:系統(tǒng)變量innodb_read_ahead_threshold代表觸發(fā)線性預讀的閾值,如果順序的訪問某個區(qū)的頁面數(shù)量超過該值,InnoDB就會異步的將下一個區(qū)的所有頁面加載到Buffer Pool,默認值56。
  • 2.2隨機預讀:系統(tǒng)變量innodb_random_read_ahead代表觸發(fā)隨機預讀的閾值,如果某個區(qū)的13個連續(xù)的頁面被加載到Buffer Pool,InnoDB就會異步的將本區(qū)其它頁面全部加載到Buffer Pool,該功能默認關閉。

綜上所述,全表掃描和預讀可能會破壞LRU鏈表,本質上就是將大量可能短期不會被訪問到的頁面加入到LRU鏈表,反而導致那些訪問頻率很高的頁面被擠掉了,導致Buffer Pool的命中率降低。

為了解決這個問題,InnoDB對LRU鏈表進行了優(yōu)化,將LRU鏈表按照一定的比例分成兩部分:存儲訪問頻率很高的Young區(qū)、存儲訪問頻率較低的Old區(qū)。系統(tǒng)變量innodb_old_blocks_pct控制了Old區(qū)所占的比例,默認值是37。也就是說,整個LRU鏈表的前約5/8部分用來存儲訪問頻率很高的緩沖頁,后約3/8部分用來存儲訪問頻率較低的緩沖頁。

將LRU鏈表劃分為兩截后,InnoDB是這樣來維護LRU鏈表的:首次加載的頁面不會直接放到LRU鏈表的表頭,而是Old區(qū)的頭部,如果該頁面后續(xù)沒有繼續(xù)訪問,會慢慢被釋放掉,而不影響Young區(qū)的頁面。如果后續(xù)再次訪問了該頁面,判斷距離上次訪問的時間,只有兩次訪問的時間間隔超過了閾值,才會把它移動到Young區(qū)頭部。

時間間隔的閾值通過系統(tǒng)變量innodb_old_blocks_time配置,默認是1000ms。

LRU鏈表經(jīng)過這么一番優(yōu)化后,我們看看是如何解決上面兩個場景的:

  • 全表掃描:全表掃描的頁面首次加載只會放在Old區(qū)頭部,雖然馬上又會訪問同一個頁面,但是時間間隔很短,因此不會移動到Young區(qū)。(每一條記錄都要訪問一次頁面)
  • 預讀:預讀首次加載的頁面只會放在Old區(qū)頭部,只要后續(xù)不再繼續(xù)訪問,就會慢慢被釋放掉。

對于Young區(qū)的緩沖頁,如果每訪問一次都要把它移動到LRU鏈表的表頭,這個操作未免也太頻繁了,因為Young區(qū)本來就是訪問頻率很高的頁面,大家互相換來換去意義不大。所以InnoDB再進一步優(yōu)化,如果訪問的緩沖頁在Young區(qū)的前1/4處,是不需要移動到表頭的,只有訪問的緩沖頁在Young區(qū)的后3/4處才會把它移動到表頭,這大大降低了鏈表節(jié)點移動的頻率。

2.6 多個實例

現(xiàn)在我們知道,Buffer Pool在物理上雖然是一塊連續(xù)的內存空間,但是邏輯上它由多條鏈表組成。在維護這些鏈表時,都需要加鎖來保證同步,在高并發(fā)場景下,這會帶來一些性能上的影響。為了解決這個問題,InnoDB支持多個Buffer Pool實例,每個實例都是獨立的,會維護自己的各種鏈表,多線程并發(fā)訪問時不會有影響,從而提高并發(fā)處理能力。

查看Buffer Pool實例個數(shù)的命令,默認是1個。

mysql> SHOW VARIABLES LIKE 'innodb_buffer_pool_instances';
+------------------------------+-------+
| Variable_name                | Value |
+------------------------------+-------+
| innodb_buffer_pool_instances | 1     |
+------------------------------+-------+

支持在配置文件中進行配置:

[server]
innodb_buffer_pool_size=2147483648
innodb_buffer_pool_instances=2

在MySQL5.7.5之前,InnoDB是不支持運行時動態(tài)調整Buffer Pool大小的,主要是因為每次調整大小,都需要向操作系統(tǒng)重新申請一個Buffer Pool,然后將數(shù)據(jù)拷貝一次,這個開銷太大了。在之后的版本中,InnoDB引入了chunk的概念來支持運行時修改Buffer Pool大小。一個Buffer Pool實例由若干個chunk組成,里面包含了若干個控制塊和緩沖頁。在調整Buffer Pool大小時,InnoDB以chunk為單位來申請內存空間和數(shù)據(jù)的拷貝。

chunk的大小由系統(tǒng)變量innodb_buffer_pool_chunk_size控制,默認是128MB,chunk本身的大小不支持運行時修改。

mysql> SHOW VARIABLES LIKE 'innodb_buffer_pool_chunk_size';
+-------------------------------+-----------+
| Variable_name                 | Value     |
+-------------------------------+-----------+
| innodb_buffer_pool_chunk_size | 134217728 |
+-------------------------------+-----------+

innodb_buffer_pool_size必須是innodb_buffer_pool_chunk_size*innodb_buffer_pool_instances的整數(shù)倍大小,目的是保證沒個Buffer Pool實例的chunk數(shù)量一致。

2.7 Buffer Pool狀態(tài)信息

說了這么多,耳聽為虛,眼見為實。如何查看MySQL運行時的Buffer Pool相關的狀態(tài)信息呢?命令是SHOW ENGINE INNODB STATUS,輸出的是InnoDB引擎的狀態(tài)信息,其中就包含Buffer Pool的狀態(tài)信息,如下:

----------------------
BUFFER POOL AND MEMORY
----------------------
Total large memory allocated 137428992
Dictionary memory allocated 268616
Buffer pool size   8191
Free buffers       7238
Database pages     953
Old database pages 371
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 919, created 34, written 36
0.00 reads/s, 0.00 creates/s, 0.00 writes/s
Buffer pool hit rate 740 / 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: 959, unzip_LRU len: 0
I/O sum[0]:cur[0], unzip sum[0]:cur[0]

  • Total large memory allocated:Buffer Pool向操作系統(tǒng)申請的總內存大小,包括控制塊大小。
  • Dictionary memory allocated:給數(shù)據(jù)字典分配的內存大小,不包含在Buffer Pool總內存大小中。
  • Buffer pool size:Buffer Pool可以容納多少緩沖頁。
  • Free buffers:Free鏈表的頁面數(shù)。
  • Database pages:LRU鏈表的頁面數(shù)。
  • Old database pages:LRU鏈表Old區(qū)域的頁面數(shù)。
  • Modified db pages:臟頁數(shù)量,即Flush鏈表的頁面數(shù)。
  • Pending reads:等待從磁盤加載到Buffer Pool的頁面數(shù)。
  • Pending writes.LRU:等待從LRU鏈表中刷新到磁盤的頁面數(shù)。
  • Pending writes.flush list:等待從Flush鏈表中刷新到磁盤的頁面數(shù)。
  • Pending writes.single page:等待以單個頁面的形式刷新到磁盤的頁面數(shù)。
  • Pages made young:LRU鏈表曾經(jīng)從Old區(qū)移動到Young區(qū)的節(jié)點數(shù)。
  • Pages made not young:再次訪問Old區(qū)的節(jié)點因為時間問題不能移動到Young區(qū)的節(jié)點數(shù)。
  • youngs/s:每秒從Old移動到Young區(qū)的節(jié)點數(shù)。
  • non-youngs/s:每秒由于時間限制不能從Old移動到Young區(qū)的節(jié)點數(shù)。
  • Pages read/created/written:讀取/創(chuàng)建/寫入了多少頁面,下一行是對應的速率。
  • Buffer pool hit rate:過去平均每訪問一千次頁面,有多少次頁面已經(jīng)被緩存到Buffer Pool。
  • young-making rate:過去平均每訪問一千次頁面,有多少次使頁面移動到Young區(qū)頭部。
  • not young-making rate:過去平均每訪問一千次頁面,有多少次沒有使頁面移動到Young區(qū)頭部。
  • LRU len:LRU鏈表的節(jié)點數(shù)。
  • I/O sum:最近50秒,讀取磁盤的總頁數(shù)。
  • I/O cur:現(xiàn)在正在讀取磁盤頁的數(shù)量。
  • I/O unzip sum:最近50秒解壓的頁面數(shù)。
  • I/O unzip cur:正在解壓的頁面數(shù)。

3. 總結

磁盤速度太慢了,如果每次讀取頁面都從磁盤加載,會導致大量的磁盤IO隨機讀,MySQL的性能勢必會受到嚴重影響。為了解決這個問題,InnoDB引入了Buffer Pool,它會在MySQL服務器啟動時申請一塊連續(xù)的內存空間,用來緩存對應的磁盤里的頁結構。每個緩沖頁都有一個與之關聯(lián)的控制塊,InnoDB為了不同的目的,將這些控制塊串聯(lián)成多條雙向鏈表,例如:Free鏈表、LRU鏈表、Flush鏈表等等。為了提高Buffer Pool的命中率,防止一些特殊的操作破壞LRU鏈表,InnoDB將LRU鏈表按照一定的比例劃分成兩截,分別是存放訪問頻率很高的頁的Young區(qū),和訪問頻率較低的頁的Old區(qū)。Buffer Pool邏輯上由這些鏈表組成,維護這些鏈表都需要加鎖保證同步,高并發(fā)下會影響性能,所以InnoDB支持配置多個Buffer Pool實例。為了在運行時支持調整Buffer Pool的大小,InnoDB又引入了chunk的概念,最后通過命令我們可以查看Buffer Pool的狀態(tài)信息。

到此這篇關于MySql InnoDB存儲引擎之Buffer Pool運行原理講解的文章就介紹到這了,更多相關MySql InnoDB Buffer Pool內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!

相關文章

  • MySQL curdate()函數(shù)的實例詳解

    MySQL curdate()函數(shù)的實例詳解

    這篇文章主要介紹了MySQL curdate()函數(shù)的實例詳解的相關資料,希望通過本文能幫助到大家理解應用MysqL curdate()的使用方法,需要的朋友可以參考下
    2017-09-09
  • MySQL中用戶授權以及刪除授權的方法

    MySQL中用戶授權以及刪除授權的方法

    這篇文章主要介紹了MySQL中用戶授權以及刪除授權的方法的相關資料,需要的朋友可以參考下
    2015-12-12
  • Mysql主從數(shù)據(jù)庫(Master/Slave)同步配置與常見錯誤

    Mysql主從數(shù)據(jù)庫(Master/Slave)同步配置與常見錯誤

    今天小編就為大家分享一篇關于Mysql主從數(shù)據(jù)庫(Master/Slave)同步配置與常見錯誤,小編覺得內容挺不錯的,現(xiàn)在分享給大家,具有很好的參考價值,需要的朋友一起跟隨小編來看看吧
    2019-03-03
  • MySQL 不等于的三種使用及區(qū)別

    MySQL 不等于的三種使用及區(qū)別

    MySQL中常用到判斷符號,而不等于是比較常用的符號,不等于主要是三種,本文主要介紹了三種的使用及區(qū)別,感興趣的同學可以了解一下
    2021-06-06
  • MYSQL METADATA LOCK(MDL LOCK)MDL鎖問題分析

    MYSQL METADATA LOCK(MDL LOCK)MDL鎖問題分析

    這篇文章主要介紹了MYSQL METADATA LOCK(MDL LOCK)MDL鎖問題分析,并通過實例給大家例句的問題處理辦法,需要的朋友參考學習下。
    2017-12-12
  • MySQL中查詢json格式的字段實例詳解

    MySQL中查詢json格式的字段實例詳解

    這篇文章主要給大家介紹了關于MySQL中查詢json格式字段的相關資料,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2021-03-03
  • 一文搞懂MySQL XA如何實現(xiàn)分布式事務

    一文搞懂MySQL XA如何實現(xiàn)分布式事務

    MySQL如何實現(xiàn)多個MySQL數(shù)據(jù)庫更新的一致性呢?那就是MySQL XA,本文就來介紹一下MySQL XA如何實現(xiàn)分布式事務,具有一定的參考價值,感興趣的可以了解一下
    2021-11-11
  • MySQL主從復制斷開的常用修復方法

    MySQL主從復制斷開的常用修復方法

    這篇文章主要介紹了MySQL主從復制斷開的常用修復方法,幫助大家更好的理解和學習使用MySQL,感興趣的朋友可以了解下
    2021-04-04
  • SQL查詢語句執(zhí)行的過程

    SQL查詢語句執(zhí)行的過程

    這篇文章主要介紹了SQL查詢語句執(zhí)行的過程,文章圍繞主題展開SQL查詢語句的相關資料,具有一定的參考價值,需要的小伙伴可以參考一下
    2022-05-05
  • 詳解MySQL中的事務與ACID特性

    詳解MySQL中的事務與ACID特性

    這篇文章主要為大家介紹了Mysql?中的事務,包括事務的基本概念和?ACID?特性、事務的隔離級別和具體實現(xiàn)方法等,并提供相應的代碼示例,希望對大家有所幫助
    2023-05-05

最新評論