22 生產(chǎn)經(jīng)驗:如何通過chunk來支持數(shù)據(jù)庫運行期間的Buffer Pool動態(tài)調(diào)整?

生產(chǎn)經(jīng)驗:如何通過chunk來支持數(shù)據(jù)庫運行期間的Buffer Pool動態(tài)調(diào)整?
1、buffer pool這種大塊頭,能在運行期間動態(tài)調(diào)整大小嗎?
上一篇文章給大家分析了一下buffer pool在多線程并發(fā)訪問的時候的一些問題,以及通過多個buffer pool是如何優(yōu)化多線程并發(fā)訪問性能的。
那么這一篇文章我們接著分析下一個問題,那就是buffer pool這種大塊頭數(shù)據(jù)結(jié)構(gòu),在數(shù)據(jù)庫運行期間,可以動態(tài)的調(diào)整他的大小嗎?
其實如果就我們講的這套原理的話,buffer pool在運行期間是不能動態(tài)的調(diào)整自己的大小的
為什么呢?因為動態(tài)調(diào)整buffer pool大小,比如buffer pool本來是8G,運行期間你給調(diào)整為16G了,此時是怎么實現(xiàn)的呢?
就是需要這個時候向操作系統(tǒng)申請一塊新的16GB的連續(xù)內(nèi)存,然后把現(xiàn)在的buffer pool中的所有緩存頁、描述數(shù)據(jù)塊、各種鏈表,都拷貝到新的16GB的內(nèi)存中去。這個過程是極為耗時的,性能很低下,是不可以接受的!
所以就目前講解的這套原理,buffer pool是絕對不能支持運行期間動態(tài)調(diào)整大小的。
2、如何基于chunk機制把buffer pool給拆小呢?
但是MySQL自然會想辦法去做一些優(yōu)化的,他實際上設(shè)計了一個chunk機制,也就是說buffer pool是由很多chunk組成的,他的大小是innodb_buffer_pool_chunk_size參數(shù)控制的,默認值就是128MB。
所以實際上我們可以來做一個假設(shè),比如現(xiàn)在我們給buffer pool設(shè)置一個總大小是8GB,然后有4個buffer pool,那么每個buffer pool就是2GB,此時每個buffer pool是由一系列的128MB的chunk組成的,也就是說每個buffer pool會有16個chunk。
然后每個buffer pool里的每個chunk里就是一系列的描述數(shù)據(jù)塊和緩存頁,每個buffer pool里的多個chunk共享一套free、flush、lru這些鏈表,此時的話,看起來可能大致如下圖所示。

在上面的圖里,可以清晰的看到,每個buffer pool里已經(jīng)有了多個chunk,每個chunk就是一系列的描述數(shù)據(jù)塊和緩存頁,這樣的話,就是把buffer pool按照chunk為單位,拆分為了一系列的小數(shù)據(jù)塊,但是每個buffer pool是共用一套free、flush、lru的鏈表的。
3、基于chunk機制是如何支持運行期間,動態(tài)調(diào)整buffer pool大小的?
那么現(xiàn)在有了上面講的這套chunk機制,就可以支持動態(tài)調(diào)整buffer pool大小了。
比如我們buffer pool現(xiàn)在總大小是8GB,現(xiàn)在要動態(tài)加到16GB,那么此時只要申請一系列的128MB大小的chunk就可以了,只要每個chunk是連續(xù)的128MB內(nèi)存就行了。然后把這些申請到的chunk內(nèi)存分配給buffer pool就行了。
有個這個chunk機制,此時并不需要額外申請16GB的連續(xù)內(nèi)存空間,然后還要把已有的數(shù)據(jù)進行拷貝。
給大家講解這個chunk機制,倒不是讓大家在數(shù)據(jù)庫運行的時候動態(tài)調(diào)整buffer pool大小,其實這不是重點,重點是大家要了解數(shù)據(jù)庫的buffer pool的真實的數(shù)據(jù)結(jié)構(gòu),是可以由多個buffer pool組成的,每個buffer pool是多個chunk組成的,然后你只要知道他運行期間可以支持動態(tài)調(diào)整大小就可以了。
4、昨日思考題解答
現(xiàn)在我們來解答一下昨天的思考題,昨天讓大家思考了一下,到底如何避免你執(zhí)行crud的時候,頻繁的發(fā)現(xiàn)緩存頁都用完了,完了還得先把一個緩存頁刷入磁盤騰出一個空閑緩存頁,然后才能從磁盤讀取一個自己需要的數(shù)據(jù)頁到緩存頁里來。
如果頻繁這么搞,那么很多crud操作,每次都要執(zhí)行兩次磁盤IO,一次是緩存頁刷入磁盤,一次是數(shù)據(jù)頁從磁盤里讀取出來,性能是很不高的。
其實結(jié)合我們了解到的buffer pool的運行原理就可以知道,如果要避免上述問題,說白了就是避免緩存頁頻繁的被使用完畢。那么我們知道實際上你在使用緩存頁的過程中,有一個后臺線程會定時把LRU鏈表冷數(shù)據(jù)區(qū)域的一些緩存頁刷入磁盤中。
所以本質(zhì)上緩存頁一邊會被你使用,一邊會被后臺線程定時的釋放掉一批。
所以如果你的緩存頁使用的很快,然后后臺線程釋放緩存頁的速度很慢,那么必然導致你頻繁發(fā)現(xiàn)緩存頁被使用完了。但是緩存頁被使用的速度你是沒法控制的,因為那是由你的Java系統(tǒng)訪問數(shù)據(jù)庫的并發(fā)程度來決定的,你高并發(fā)訪問數(shù)據(jù)庫,緩存頁必然使用的很快了!
然后你后臺線程定時釋放一批緩存頁,這個過程也很難去優(yōu)化,因為你要是釋放的過于頻繁了,那么后臺線程執(zhí)行磁盤IO過于頻繁,也會影響數(shù)據(jù)庫的性能。
所以這里的關(guān)鍵點就在于,你的buffer pool有多大!
如果你的數(shù)據(jù)庫要抗高并發(fā)的訪問,那么你的機器必然要配置很大的內(nèi)存空間,起碼是32GB以上的,甚至64GB或者128GB。此時你就可以給你的buffer pool設(shè)置很大的內(nèi)存空間,比如20GB,48GB,甚至80GB。
這樣的話,你會發(fā)現(xiàn)高并發(fā)場景下,數(shù)據(jù)庫的buffer pool緩存頁頻繁的被使用,但是你后臺線程也在定時釋放一些緩存頁,那么綜合下來,空閑的緩存頁還是會以一定的速率逐步逐步的減少。
因為你的buffer pool內(nèi)存很大,所以空閑緩存頁是很多很多的,即使你的空閑緩存頁逐步的減少,也可能需要較長時間才會發(fā)現(xiàn)緩存頁用完了,此時才會出現(xiàn)一次crud操作執(zhí)行的時候,先刷緩存頁到磁盤,再讀取數(shù)據(jù)頁到緩存頁來,這種情況是不會出現(xiàn)的太頻繁的!
而一旦你的數(shù)據(jù)庫高峰過去,此時緩存頁被使用的速率下降了很多很多,然后后臺線程會定是基于flush鏈表和lru鏈表不停的釋放緩存頁,那么你的空閑緩存頁的數(shù)量又會在數(shù)據(jù)庫低峰的時候慢慢的增加了。
所以線上的MySQL在生產(chǎn)環(huán)境中,buffer pool的大小、buffer pool的數(shù)量,這都是要用心設(shè)置和優(yōu)化的,因為對MySQL的性能和并發(fā)能力,都會有較大的影響。
5、實踐思考題
請每位同學,去看看自己負責的系統(tǒng)的buffer pool大小、buffer pool數(shù)量、chunk大小,然后看看自己的數(shù)據(jù)庫的機器配置,思考一下,當前設(shè)置是否合理?為什么要這樣設(shè)置?
End
專欄版權(quán)歸公眾號儒猿技術(shù)窩所有
未經(jīng)許可不得傳播,如有侵權(quán)將追究法律責任