偷偷摘套内射激情视频,久久精品99国产国产精,中文字幕无线乱码人妻,中文在线中文a,性爽19p

MySQL系列:緩沖池Buffer Pool的設(shè)計(jì)思想

數(shù)據(jù)庫(kù) MySQL
本節(jié)主要講了三鏈表的作用,free鏈表記錄空閑緩存頁(yè),flush鏈表記錄臟頁(yè),即待刷盤緩存頁(yè),當(dāng)free鏈表沒(méi)有空閑時(shí),lru鏈表淘汰最近不常用的緩存頁(yè)。

1. 回顧

我們主要講了InnoDB的存儲(chǔ)引擎,其中主要的一個(gè)組件就是緩存池Buffer Pool,緩存了磁盤的真實(shí)數(shù)據(jù),然后基于緩存做增刪改查操作,同時(shí)配合了后續(xù)的redo log、刷磁盤等機(jī)制和操作。如下圖:

這一篇,深入該組件內(nèi)部,學(xué)習(xí)一下其設(shè)計(jì)思想。

2. Buffer Pool數(shù)據(jù)結(jié)構(gòu)

Buffer Pool本質(zhì)其實(shí)就是數(shù)據(jù)庫(kù)的一個(gè)內(nèi)存組件,默認(rèn)情況下是128MB,如果我們的數(shù)據(jù)庫(kù)如果是16核32G的機(jī)器,那么你就可以給Buffer Pool分配個(gè)2GB的內(nèi)存,使用下面的配置就可以了
innodb_buffer_pool_size = 2147483648

磁盤加載數(shù)據(jù)頁(yè)到緩存,數(shù)據(jù)頁(yè)在緩存中被定義為緩存頁(yè),緩存頁(yè)與緩存頁(yè)默認(rèn)16KB,每個(gè)緩存頁(yè)有對(duì)應(yīng)的描述數(shù)據(jù),描述了這個(gè)數(shù)據(jù)頁(yè)所屬的表空間、數(shù)據(jù)頁(yè)的編號(hào)、這個(gè)緩存頁(yè)在Buffer Pool中的地址等。在Buffer Pool中,每個(gè)緩存頁(yè)的描述數(shù)據(jù)放在最前面,各個(gè)緩存頁(yè)放在后面。Buffer Pool中的描述數(shù)據(jù)大概相當(dāng)于緩存頁(yè)大小的5%左右,也就是每個(gè)描述數(shù)據(jù)大概是800個(gè)字節(jié)左右的大小,然后假設(shè)你設(shè)置的buffer pool大小是128MB,實(shí)際上Buffer Pool真正的最終大小會(huì)超出一些,可能有個(gè)130多MB的樣子,因?yàn)樗锩孢€要存放每個(gè)緩存頁(yè)的描述數(shù)據(jù)。

數(shù)據(jù)結(jié)構(gòu)如下圖:

3. free鏈表

接著我們來(lái)看下一個(gè)問(wèn)題,當(dāng)數(shù)據(jù)庫(kù)運(yùn)行起來(lái)之后,肯定會(huì)不停的執(zhí)行增刪改查的操作,此時(shí)就需要不停的從磁盤上讀取一個(gè)一個(gè)的數(shù)據(jù)頁(yè)放入Buffer Pool中的對(duì)應(yīng)的緩存頁(yè)里去,把數(shù)據(jù)緩存起來(lái),那么以后就可以對(duì)這個(gè)數(shù)據(jù)在內(nèi)存里執(zhí)行增刪改查了。但是此時(shí)在從磁盤上讀取數(shù)據(jù)頁(yè)放入Buffer Pool中的緩存頁(yè)的時(shí)候,必然涉及到一個(gè)問(wèn)題,就是哪些緩存頁(yè)是空閑的?

所以數(shù)據(jù)庫(kù)會(huì)為Buffer Pool設(shè)計(jì)一個(gè)free鏈表,他是一個(gè)雙向鏈表數(shù)據(jù)結(jié)構(gòu),這個(gè)free鏈表里,每個(gè)節(jié)點(diǎn)就是一個(gè)空閑的緩存頁(yè)的描述數(shù)據(jù)塊的地址,也就是說(shuō),只要你一個(gè)緩存頁(yè)是空閑的,那么他的描述數(shù)據(jù)塊就會(huì)被放入這個(gè)free鏈表中。如果要把數(shù)據(jù)頁(yè)寫入緩存頁(yè),就從鏈表中摘除這個(gè)節(jié)點(diǎn),將該節(jié)點(diǎn)的描述數(shù)據(jù)寫到Buffer pool,再把寫入對(duì)應(yīng)的緩存頁(yè)。

寫入之前還會(huì)判斷該數(shù)據(jù)頁(yè)是否已經(jīng)被緩存,引入哈希表數(shù)據(jù)結(jié)構(gòu),他會(huì)用表空間號(hào)+數(shù)據(jù)頁(yè)號(hào),作為一個(gè)key,然后緩存頁(yè)的地址作為value,當(dāng)要使用一個(gè)數(shù)據(jù)頁(yè)的時(shí)候,通過(guò)“表空間號(hào)+數(shù)據(jù)頁(yè)號(hào)”作為key去這個(gè)哈希表里查一下,如果沒(méi)有就讀取數(shù)據(jù)頁(yè),如果已經(jīng)有了,就說(shuō)明數(shù)據(jù)頁(yè)已經(jīng)被緩存了。

如下圖所示:

4. flush鏈表

更新過(guò)的緩存頁(yè)與磁盤不一致,需要刷到磁盤的緩沖頁(yè)構(gòu)成的雙向鏈表;也叫待刷盤的臟頁(yè)數(shù)據(jù)頁(yè)鏈表;如下圖所示

5. lru鏈表

如果所有的緩存頁(yè)都被塞了數(shù)據(jù)了,此時(shí)無(wú)法從磁盤上加載新的數(shù)據(jù)頁(yè)到緩存頁(yè)里去了,那么此時(shí)你只有一個(gè)辦法,就是淘汰掉一些緩存頁(yè)。引入LRU鏈表來(lái)判斷哪些緩存頁(yè)是不常用的。這個(gè)所謂的LRU就是Least Recently Used,最近最少使用的意思。

假設(shè)某個(gè)緩存頁(yè)的描述數(shù)據(jù)塊本來(lái)在LRU鏈表的尾部,后續(xù)你只要查詢或者修改了這個(gè)緩存頁(yè)的數(shù)據(jù),也要把這個(gè)緩存頁(yè)挪動(dòng)到LRU鏈表的頭部去,也就是說(shuō)最近被訪問(wèn)過(guò)的緩存頁(yè),一定在LRU鏈表的頭部。如下圖所示

淘汰不常用的緩存頁(yè),尾部淘汰冷數(shù)據(jù),頭部插入熱數(shù)據(jù)。

6. 數(shù)據(jù)頁(yè)預(yù)讀帶來(lái)的問(wèn)題

MySQL為提升讀取性能,引入了預(yù)讀機(jī)制,就是當(dāng)從磁盤上加載一個(gè)數(shù)據(jù)頁(yè)的時(shí)候,他可能會(huì)連帶著把這個(gè)數(shù)據(jù)頁(yè)相鄰的其他數(shù)據(jù)頁(yè),也加載到緩存里去!
舉個(gè)例子,假設(shè)現(xiàn)在有兩個(gè)空閑緩存頁(yè),然后在加載一個(gè)數(shù)據(jù)頁(yè)的時(shí)候,連帶著把他的一個(gè)相鄰的數(shù)據(jù)頁(yè)也加載到緩存里去了,正好每個(gè)數(shù)據(jù)頁(yè)放入一個(gè)空閑緩存頁(yè)!但是接下來(lái)呢,實(shí)際上只有一個(gè)緩存頁(yè)是被訪問(wèn)了,另外一個(gè)通過(guò)預(yù)讀機(jī)制加載的緩存頁(yè),其實(shí)并沒(méi)有人訪問(wèn),此時(shí)這兩個(gè)緩存頁(yè)可都在LRU鏈表的前面。

我們可以看到,這個(gè)圖里很清晰的表明了,前兩個(gè)緩存頁(yè)都是剛加載進(jìn)來(lái)的,但是此時(shí)第二個(gè)緩存頁(yè)是通過(guò)預(yù)讀機(jī)制捎帶著加載進(jìn)來(lái)的,他也放到了鏈表的前面,但是他實(shí)際沒(méi)人訪問(wèn)他。除了第二個(gè)緩存頁(yè)之外,第一個(gè)緩存頁(yè),以及尾巴上兩個(gè)緩存頁(yè),都是一直有人訪問(wèn)的那種緩存頁(yè),只不過(guò)上圖代表的是剛剛把頭部?jī)蓚€(gè)緩存頁(yè)加載進(jìn)來(lái)的時(shí)候的一個(gè)LRU鏈表當(dāng)時(shí)的情況。

哪些場(chǎng)景會(huì)導(dǎo)致預(yù)讀呢?

(1)有一個(gè)參數(shù)是innodb_read_ahead_threshold,他的默認(rèn)值是56,意思就是如果順序的訪問(wèn)了一個(gè)區(qū)里的多個(gè)數(shù)據(jù)頁(yè),訪問(wèn)的數(shù)據(jù)頁(yè)的數(shù)量超過(guò)了這個(gè)閾值,此時(shí)就會(huì)觸發(fā)預(yù)讀機(jī)制,把下一個(gè)相鄰區(qū)中的所有數(shù)據(jù)頁(yè)都加載到緩存里去。
(2)如果Buffer Pool里緩存了一個(gè)區(qū)里的13個(gè)連續(xù)的數(shù)據(jù)頁(yè),而且這些數(shù)據(jù)頁(yè)都是比較頻繁會(huì)被訪問(wèn)的,此時(shí)就會(huì)直接觸發(fā)預(yù)讀機(jī)制,把這個(gè)區(qū)里的其他的數(shù)據(jù)頁(yè)都加載到緩存里去。

這個(gè)機(jī)制是通過(guò)參數(shù)innodb_random_read_ahead來(lái)控制的,他默認(rèn)是OFF,也就是這個(gè)規(guī)則是關(guān)閉的。

(3)接著我們講另外一種可能導(dǎo)致頻繁被訪問(wèn)的緩存頁(yè)被淘汰的場(chǎng)景,那就是全表掃描。

這個(gè)所謂的全表掃描,意思就是類似如下的SQL語(yǔ)句:SELECT * FROM USERS,此時(shí)他沒(méi)加任何一個(gè)where條件,會(huì)導(dǎo)致他直接一下子把這個(gè)表里所有的數(shù)據(jù)頁(yè),都從磁盤加載到Buffer Pool里去。這個(gè)時(shí)候他可能會(huì)一下子就把這個(gè)表的所有數(shù)據(jù)頁(yè)都一一裝入各個(gè)緩存頁(yè)里去!此時(shí)可能LRU鏈表中排在前面的一大串緩存頁(yè),都是全表掃描加載進(jìn)來(lái)的緩存頁(yè)!那么如果這次全表掃描過(guò)后,后續(xù)幾乎沒(méi)用到這個(gè)表里的數(shù)據(jù)呢?此時(shí)LRU鏈表的尾部,可能全部都是之前一直被頻繁訪問(wèn)的那些緩存頁(yè)!然后當(dāng)你要淘汰掉一些緩存頁(yè)騰出空間的時(shí)候,就會(huì)把LRU鏈表尾部一直被頻繁訪問(wèn)的緩存頁(yè)給淘汰掉了,而留下了之前全表掃描加載進(jìn)來(lái)的大量的不經(jīng)常訪問(wèn)的緩存頁(yè)。

7. 解決預(yù)讀帶來(lái)的問(wèn)題

所以為了解決上一講我們說(shuō)的簡(jiǎn)單的LRU鏈表的問(wèn)題,真正MySQL在設(shè)計(jì)LRU鏈表的時(shí)候,采取的實(shí)際上是冷熱數(shù)據(jù)分離的思想。
所以真正的LRU鏈表,會(huì)被拆分為兩個(gè)部分,一部分是熱數(shù)據(jù),一部分是冷數(shù)據(jù),這個(gè)冷熱數(shù)據(jù)的比例是由innodb_old_blocks_pct參數(shù)控制的,他默認(rèn)是37,也就是說(shuō)冷數(shù)據(jù)占比37%。

第一次被加載了數(shù)據(jù)的緩存頁(yè),都會(huì)不停的移動(dòng)到冷數(shù)據(jù)區(qū)域的鏈表頭部。冷數(shù)據(jù)區(qū)域的緩存頁(yè)什么時(shí)候會(huì)放到熱數(shù)據(jù)區(qū)域呢?實(shí)際上肯定很多人會(huì)想,只要對(duì)冷數(shù)據(jù)區(qū)域的緩存頁(yè)進(jìn)行了一次訪問(wèn),就立馬把這個(gè)緩存頁(yè)放到熱數(shù)據(jù)區(qū)域的頭部行不行呢?

其實(shí)這也是不合理的,如果你剛加載了一個(gè)數(shù)據(jù)頁(yè)到那個(gè)緩存頁(yè),他是在冷數(shù)據(jù)區(qū)域的鏈表頭部,然后立馬(在1ms以內(nèi))就訪問(wèn)了一下這個(gè)緩存頁(yè),之后就再也不訪問(wèn)他了呢?難道這種情況你也要把那個(gè)緩存頁(yè)放到熱數(shù)據(jù)區(qū)域的頭部嗎?

所以MySQL設(shè)定了一個(gè)規(guī)則,他設(shè)計(jì)了一個(gè)innodb_old_blocks_time參數(shù),默認(rèn)值1000,也就是1000毫秒。也就是說(shuō),必須是一個(gè)數(shù)據(jù)頁(yè)被加載到緩存頁(yè)之后,在1s之后,你訪問(wèn)這個(gè)緩存頁(yè),他才會(huì)被挪動(dòng)到熱數(shù)據(jù)區(qū)域的鏈表頭部去。因?yàn)榧僭O(shè)你加載了一個(gè)數(shù)據(jù)頁(yè)到緩存去,然后過(guò)了1s之后你還訪問(wèn)了這個(gè)緩存頁(yè),說(shuō)明你后續(xù)很可能會(huì)經(jīng)常要訪問(wèn)它,這個(gè)時(shí)間限制就是1s,因此只有1s后你訪問(wèn)了這個(gè)緩存頁(yè),他才會(huì)給你把緩存頁(yè)放到熱數(shù)據(jù)區(qū)域的鏈表頭部去。

該思想通過(guò)冷熱分離+時(shí)間訪問(wèn)限制,解決了誤淘汰熱數(shù)據(jù)的問(wèn)題。吸收冷熱隔離思想,結(jié)合項(xiàng)目場(chǎng)景,可以優(yōu)化緩存中的冷熱數(shù)據(jù)。

LRU鏈表的熱數(shù)據(jù)區(qū)域是如何進(jìn)行優(yōu)化的呢?

熱數(shù)據(jù)區(qū)域里的緩存頁(yè)可能是經(jīng)常被訪問(wèn)的,所以這么頻繁的進(jìn)行移動(dòng)是不是性能也并不是太好?也沒(méi)這個(gè)必要。

所以說(shuō),LRU鏈表的熱數(shù)據(jù)區(qū)域的訪問(wèn)規(guī)則被優(yōu)化了一下,即你只有在熱數(shù)據(jù)區(qū)域的后3/4部分的緩存頁(yè)被訪問(wèn)了,才會(huì)給你移動(dòng)到鏈表頭部去。如果你是熱數(shù)據(jù)區(qū)域的前面1/4的緩存頁(yè)被訪問(wèn),他是不會(huì)移動(dòng)到鏈表頭部去的。舉個(gè)例子,假設(shè)熱數(shù)據(jù)區(qū)域的鏈表里有100個(gè)緩存頁(yè),那么排在前面的25個(gè)緩存頁(yè),他即使被訪問(wèn)了,也不會(huì)移動(dòng)到鏈表頭部去的。但是對(duì)于排在后面的75個(gè)緩存頁(yè),他只要被訪問(wèn),就會(huì)移動(dòng)到鏈表頭部去。這樣的話,他就可以盡可能的減少鏈表中的節(jié)點(diǎn)移動(dòng)了。

8. 總結(jié)

本節(jié)主要講了三鏈表的作用,free鏈表記錄空閑緩存頁(yè),flush鏈表記錄臟頁(yè),即待刷盤緩存頁(yè),當(dāng)free鏈表沒(méi)有空閑時(shí),lru鏈表淘汰最近不常用的緩存頁(yè)。三鏈表動(dòng)態(tài)執(zhí)行過(guò)程可以表述為:free鏈表移除結(jié)點(diǎn),lru鏈表冷數(shù)據(jù)區(qū)頭部加入該節(jié)點(diǎn);如果修改了緩存頁(yè),flush加入這個(gè)臟頁(yè),lru表中還可能會(huì)從冷數(shù)據(jù)區(qū)域移動(dòng)到熱數(shù)據(jù)區(qū)域的頭部去;如果查詢了緩存頁(yè),會(huì)把這個(gè)緩存頁(yè)在lru鏈表中移動(dòng)到熱數(shù)據(jù)區(qū)域去,或者在熱數(shù)據(jù)區(qū)域中也有可能會(huì)移動(dòng)到頭部去??傊?,要么free鏈表移除節(jié)點(diǎn),flush鏈表加節(jié)點(diǎn),lru鏈表移動(dòng)節(jié)點(diǎn);要么free加節(jié)點(diǎn),flush減節(jié)點(diǎn),lru減節(jié)點(diǎn)。

責(zé)任編輯:武曉燕 來(lái)源: 今日頭條
相關(guān)推薦

2022-03-22 15:05:15

MySQL緩沖池

2019-06-24 05:05:40

緩沖池查詢數(shù)據(jù)InnoDB

2021-03-01 18:37:15

MySQL存儲(chǔ)數(shù)據(jù)

2024-10-23 08:47:46

2022-03-30 09:23:15

MySQL緩沖

2024-07-17 09:10:27

2025-04-08 08:20:00

2022-10-12 08:52:00

內(nèi)存緩沖管理

2023-05-03 21:34:34

MySQL狀態(tài)變量

2021-11-29 09:38:12

設(shè)計(jì)模式對(duì)象池模式Object Pool

2022-03-26 08:49:13

MySQL數(shù)據(jù)存儲(chǔ)

2011-08-30 12:51:19

MySQL線程緩沖池

2019-06-26 06:31:56

緩沖緩沖池查詢數(shù)據(jù)

2010-05-07 19:15:18

Oracle flas

2011-07-26 15:30:32

jQuery

2022-05-13 09:02:34

LinuxBufferCache

2011-06-07 08:58:31

javascript

2010-05-05 17:45:12

IBM Unix

2025-01-23 08:33:27

2012-12-06 10:00:48

InnoDBMySQL
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)