1、Buffer Pool 概述Buffer Pool 是什么?從字面上看是緩存池的意思,沒(méi)錯(cuò),它其實(shí)也就是緩存池的意思。它是 MySQL 當(dāng)中至關(guān)重要的一個(gè)組件,可以這么說(shuō),MySQL的所有的增刪改的操作都是在 Buffer Pool 中執(zhí)行的。 但是數(shù)據(jù)不是在磁盤中的嗎?怎么會(huì)和緩存池又有什么關(guān)系呢?那是因?yàn)槿绻?MySQL的操作都在磁盤中進(jìn)行,那很顯然效率是很低的,效率為什么低?因?yàn)閿?shù)據(jù)庫(kù)要從磁盤中拿數(shù)據(jù)啊,那肯定就需要IO啊,并且數(shù)據(jù)庫(kù)并不知道它將要查找的數(shù)據(jù)是磁盤的哪個(gè)位置,所以這就需要進(jìn)行隨機(jī)IO,那這個(gè)性能簡(jiǎn)直就別玩了。所以 MySQL對(duì)數(shù)據(jù)的操作都是在內(nèi)存中進(jìn)行的,也就是在 Buffer Pool 這個(gè)內(nèi)存組件中。 實(shí)際上他就好比是 Redis,因?yàn)?Redis 是一個(gè)內(nèi)存是數(shù)據(jù)庫(kù),他的操作就都是在內(nèi)存中進(jìn)行的,并且會(huì)有一定的策略將其持久化到磁盤中。那 Buffer Pool 的內(nèi)存結(jié)構(gòu)具體是什么樣子的,那么多的增刪改操作難道數(shù)據(jù)要一直在內(nèi)存中嗎?既然說(shuō)類似 redis 緩存,那是不是也像 redis 一樣也有一定的淘汰策略呢? 本篇文章,會(huì)詳細(xì)的介紹 Buffer Pool 的內(nèi)存結(jié)構(gòu),讓大家徹底明白這里面的每一步執(zhí)行流程。我們先看一下 MySQL從加載磁盤文件到完成提交一個(gè)事務(wù)的整個(gè)流程。我們先來(lái)看一個(gè)總體的流程圖,從數(shù)據(jù)在磁盤中被加載到緩存池中,然后經(jīng)過(guò)一些列的操作最終又被刷入到磁盤的一個(gè)過(guò)程,都經(jīng)歷了哪些事情,這個(gè)圖不明白沒(méi)有關(guān)系,因?yàn)楸疚闹攸c(diǎn)是 Buffer Pool 這個(gè)整體的流程就是讓大家稍微有個(gè)印象。 2、Buffer Pool 有多大Buffer Pool 是 InnoDB 中的一塊內(nèi)存區(qū)域,他一定是有自己的大小的,且大小默認(rèn)是 128M,不過(guò)這個(gè)容量似乎有點(diǎn)小了,大家的自己的生產(chǎn)環(huán)境可以根據(jù)實(shí)際的內(nèi)存大小進(jìn)行調(diào)整,參數(shù)為:innodb_buffer_pool_size=2147483648 單位是字節(jié), # 查看和調(diào)整innodb_buffer_pool_size1. 查看@@innodb_buffer_pool_size大小,單位字節(jié)SELECT @@innodb_buffer_pool_size/1024/1024/1024; #字節(jié)轉(zhuǎn)為G2. 在線調(diào)整InnoDB緩沖池大小,如果不設(shè)置,默認(rèn)為128Mset global innodb_buffer_pool_size = 4227858432; ##單位字節(jié) 他在 InnoDB 中的整體結(jié)構(gòu)大概是這樣子的 3、數(shù)據(jù)頁(yè)剛剛介紹到 MySQL在執(zhí)行增刪改的時(shí)候數(shù)據(jù)是會(huì)被加載到 Buffer Pool 中的,既然這樣數(shù)據(jù)是怎么被加載進(jìn)來(lái)的,是一條一條還是說(shuō)是以其他的形式呢。我們操作的數(shù)據(jù)都是以表 行的方式,而表 行僅僅是邏輯上的概念,MySQL并不會(huì)像我們一樣去操作行數(shù)據(jù),而是抽象出來(lái)一個(gè)一個(gè)的數(shù)據(jù)頁(yè)概念,每個(gè)數(shù)據(jù)頁(yè)的大小默認(rèn)是 16KB,這些參數(shù)都是可以調(diào)整的。但是建議使用默認(rèn)的就好,畢竟 MySQL能做到極致的都已經(jīng)做了。每個(gè)數(shù)據(jù)頁(yè)存放著多條的數(shù)據(jù),MySQL在執(zhí)行增刪改首先會(huì)定位到這條數(shù)據(jù)所在數(shù)據(jù)頁(yè),然后會(huì)將數(shù)據(jù)所在的數(shù)據(jù)頁(yè)加載到 Buffer Pool 中。 4、緩存頁(yè)當(dāng)數(shù)據(jù)頁(yè)被加載到緩沖池中后,Buffer Pool 中也有叫緩存頁(yè)的概念與其一一對(duì)應(yīng),大小同樣是 16KB,但是 MySQL還為每個(gè)緩存也開辟額外的一些空間,用來(lái)描述對(duì)應(yīng)的緩存頁(yè)的一些信息,例如:數(shù)據(jù)頁(yè)所屬的表空間,數(shù)據(jù)頁(yè)號(hào),這些描述數(shù)據(jù)塊的大小大概是緩存頁(yè)的15%左右(約800KB)。
5、Free鏈表上面是說(shuō)了每個(gè)數(shù)據(jù)頁(yè)會(huì)被加載到一個(gè)緩存頁(yè)中,但是加載的時(shí)候 MySQL是如何知道那個(gè)緩存頁(yè)有數(shù)據(jù),那個(gè)緩存頁(yè)沒(méi)有數(shù)據(jù)呢?換句話說(shuō), MySQL是怎么區(qū)分哪些緩存頁(yè)是空閑的狀態(tài),是可以用來(lái)存放數(shù)據(jù)頁(yè)的。 為了解決這個(gè)問(wèn)題, MySQL 為 Buffer Pool 設(shè)計(jì)了一個(gè)雙向鏈表— free鏈表,這個(gè) free 鏈表的作用就是用來(lái)保存空閑緩存頁(yè)的描述塊(這句話這么說(shuō)其實(shí)不嚴(yán)謹(jǐn),換句話:每個(gè)空閑緩存頁(yè)的描述數(shù)據(jù)組成一個(gè)雙向鏈表,這個(gè)鏈表就是free鏈表)。之所以說(shuō)free鏈表的作用就是用來(lái)保存空閑緩存頁(yè)的描述數(shù)據(jù)是為了先讓大家明白 free 鏈表的作用,另外 free 鏈表還會(huì)有一個(gè)基礎(chǔ)節(jié)點(diǎn),他會(huì)引用該鏈表的頭結(jié)點(diǎn)和尾結(jié)點(diǎn),還會(huì)記錄節(jié)點(diǎn)的個(gè)數(shù)(也就是可用的空閑的緩存頁(yè)的個(gè)數(shù))。 這個(gè)時(shí)候,他可以用下面的圖片來(lái)描述: 當(dāng)加載數(shù)據(jù)頁(yè)到緩存池中的時(shí)候, MySQL會(huì)從 free 鏈表中獲取一個(gè)描述數(shù)據(jù)的信息,根據(jù)描述節(jié)點(diǎn)的信息拿到其對(duì)應(yīng)的緩存頁(yè),然后將數(shù)據(jù)頁(yè)信息放到該緩存頁(yè)中,同時(shí)將鏈表中的該描述數(shù)據(jù)的節(jié)點(diǎn)移除。這就是數(shù)據(jù)頁(yè)被讀取 Buffer Pool 中的緩存頁(yè)的過(guò)程。 但 MySQL是怎么知道哪些數(shù)據(jù)頁(yè)已經(jīng)被緩存了,哪些沒(méi)有被緩存呢。實(shí)際上數(shù)據(jù)庫(kù)中還有后一個(gè)哈希表結(jié)構(gòu),他的作用是用來(lái)存儲(chǔ)表空間號(hào) 數(shù)據(jù)頁(yè)號(hào)作為數(shù)據(jù)頁(yè)的key,緩存頁(yè)對(duì)應(yīng)的地址作為其value,這樣數(shù)據(jù)在加載的時(shí)候就會(huì)通過(guò)哈希表中的key來(lái)確定數(shù)據(jù)頁(yè)是否被緩存了。 6、Flush鏈表MySql 在執(zhí)行增刪改的時(shí)候會(huì)一直將數(shù)據(jù)以數(shù)據(jù)頁(yè)的形式加載到 Buffer Pool 的緩存頁(yè)中,增刪改的操作都是在內(nèi)存中執(zhí)行的,然后會(huì)有一個(gè)后臺(tái)的線程數(shù)將臟數(shù)據(jù)刷新到磁盤中,但是后臺(tái)的線程肯定是需要知道應(yīng)該刷新哪些啊。 針對(duì)這個(gè)問(wèn)題,MySQL設(shè)計(jì)出了 Flush 鏈表,他的作用就是記錄被修改過(guò)的臟數(shù)據(jù)所在的緩存頁(yè)對(duì)應(yīng)的描述數(shù)據(jù)。如果內(nèi)存中的數(shù)據(jù)和數(shù)據(jù)庫(kù)和數(shù)據(jù)庫(kù)中的數(shù)據(jù)不一樣,那這些數(shù)據(jù)我們就稱之為臟數(shù)據(jù),臟數(shù)據(jù)之所以叫臟數(shù)據(jù),本質(zhì)上就是被緩存到緩存池中的數(shù)據(jù)被修改了,但是還沒(méi)有刷新到磁盤中。 同樣的這些已經(jīng)被修改了的數(shù)據(jù)所在的緩存頁(yè)的描述數(shù)據(jù)會(huì)被維護(hù)到 Flush 中(其實(shí)結(jié)構(gòu)和 free 鏈表是一樣的),所以 Flush 中維護(hù)的是一些臟數(shù)據(jù)數(shù)據(jù)描述(準(zhǔn)確地說(shuō)是臟數(shù)據(jù)的所在的緩存頁(yè)的數(shù)據(jù)描述) 另外,當(dāng)某個(gè)臟數(shù)據(jù)頁(yè)頁(yè)被刷新到磁盤后,其空間就騰出來(lái)了,然后又會(huì)跑到 Free 鏈表中了。 7、LRU鏈表如果系統(tǒng)一直在進(jìn)行數(shù)據(jù)庫(kù)的增刪改操作,數(shù)據(jù)庫(kù)內(nèi)部的基本流程就是: 我們還拿 redis 類做類比,以便更好的幫助大家明白其原理。Flush 的作用其實(shí)類似 redis 的 key 設(shè)置的過(guò)期時(shí)間,所以一般情況下,redis 內(nèi)存不會(huì)不夠使用,但是總有特殊的情況,問(wèn)題往往就是在這種極端和邊邊角角的情況下產(chǎn)生的。 如果 redis 的內(nèi)存不夠使用了,是不是自己還有一定的淘汰策略?最基本的準(zhǔn)則就是淘汰掉不經(jīng)常使用到的key。Buffer Pool 也類似,它也會(huì)有內(nèi)存不夠使用的情況,它是通過(guò) LRU 鏈表來(lái)維護(hù)的。LRU 即 Least Recently Uesd(最近最少使用)。 MySql 會(huì)把最近使用最少的緩存頁(yè)數(shù)據(jù)刷入到磁盤去,那 MySql 如何判斷出 LRU 數(shù)據(jù)的呢?為此 MySql 專門設(shè)計(jì)了 LRU 鏈表,還引入了另一個(gè)概念:緩存命中率 # 緩存命中率 可以理解為緩存被使用到的頻率,舉個(gè)例子來(lái)說(shuō):現(xiàn)在有兩個(gè)緩存頁(yè),在100次請(qǐng)求中A緩存頁(yè)被命中了20次,B緩存頁(yè)被命中了2次,很顯然A緩存頁(yè)的命中率更高,這也就意味著A在未來(lái)還會(huì)被使用到的可能性比較大,而B就會(huì)被 MySQL 認(rèn)為基本不會(huì)被使用到; 說(shuō)到這里,那LRU究竟是怎么工作的。假設(shè) MySQL在將數(shù)據(jù)加載到緩存池的時(shí)候,他會(huì)將被加載進(jìn)來(lái)的緩存頁(yè)按照被加載進(jìn)來(lái)的順序插入到LRU鏈表的頭部(就是鏈表的頭插法),假設(shè) MySQL現(xiàn)在先后分別加載A、B、C數(shù)據(jù)頁(yè)到緩存頁(yè)A、B、C中,然后 LRU 的鏈表大致是這樣子的。 現(xiàn)在又來(lái)了一個(gè)請(qǐng)求,假設(shè)查詢到的數(shù)據(jù)是已經(jīng)被緩存在緩存頁(yè)B中,這時(shí)候 MySQL就會(huì)將B緩存頁(yè)對(duì)應(yīng)的描述信息插入到LRU鏈表的頭部,如下圖: 然后又來(lái)了一個(gè)請(qǐng)求,數(shù)據(jù)是已經(jīng)被緩存在了緩存頁(yè)C中,然后LRU會(huì)變成這樣子: 說(shuō)到底,每次查詢數(shù)據(jù)的時(shí)候如果數(shù)據(jù)已經(jīng)在緩存頁(yè)中,那么就會(huì)將該緩存頁(yè)對(duì)應(yīng)的描述信息放到LRU鏈表的頭部,如果不在緩存頁(yè)中,就去磁盤中查找,如果查找到了,就將其加載到緩存中,并將該數(shù)據(jù)對(duì)應(yīng)的緩存頁(yè)的描述信息插入到LRU鏈表的頭部。也就是說(shuō)最近使用的緩存頁(yè)都會(huì)排在前面,而排在后面的說(shuō)明是不經(jīng)常被使用到的。 最后,如果 Buffer Pool 不夠使用了,那么 MySQL就會(huì)將 LRU 鏈表中的尾節(jié)點(diǎn)刷入到磁盤中,以及 Buffer Pool 騰出內(nèi)存空間。來(lái)個(gè)整體的流程圖給大家看下 8、LRU鏈表帶來(lái)的麻煩這里的麻煩指的是就是 MySQL本身的預(yù)讀機(jī)制帶來(lái)的問(wèn)題
上圖能夠看到B的相鄰也被加載到了C描述數(shù)據(jù)的前面,而實(shí)際上C的命中率比B的相鄰頁(yè)高多了,這就是LRU本身帶來(lái)的問(wèn)題。 # 哪些情況會(huì)觸發(fā)預(yù)讀機(jī)制 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è)都加載到緩存里去(這種就是:線性預(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è)都加載到緩存里去(這種就是:隨機(jī)預(yù)讀)隨機(jī)預(yù)讀是通過(guò):innodb_random_read_ahead 來(lái)控制的,默認(rèn)是OFF即關(guān)閉的(MySQL 5.5已經(jīng)基本飛起該功能,應(yīng)為他會(huì)帶來(lái)不必要的麻煩,這里也不推薦大家開啟,說(shuō)出來(lái)的目的是讓大家了解下有這么個(gè)東西) 還有一種情況是 SELECT * FROM students 這種直接全表掃描的,會(huì)直接加載表中的所有的數(shù)據(jù)到緩存中,這些數(shù)據(jù)基本是加載的時(shí)候查詢一次,后面就基本使用不到了,但是加載這么多數(shù)據(jù)到鏈表的頭部就將其他的經(jīng)常命中的緩存頁(yè)直接全擠到后面去了。 以上種種跡象表明,預(yù)讀機(jī)制帶來(lái)的問(wèn)題還是蠻大的,既然這么大,那 MySQL為什么還要進(jìn)入預(yù)讀機(jī)制呢,說(shuō)到底還是為了提高效率,**一種新的技術(shù)的引進(jìn),往往帶來(lái)新的挑戰(zhàn)**,下面我們就一起來(lái)看下 MySQL是如何解決預(yù)加載所帶來(lái)的麻煩的。 9、基于冷熱數(shù)據(jù)分離的LRU鏈表所謂的冷熱分離,就是將 LRU 鏈表分成兩部分,一部分是經(jīng)常被使用到的熱數(shù)據(jù),另一部分是被加載進(jìn)來(lái)但是很少使用的冷數(shù)據(jù)。通過(guò)參數(shù)innodb_old_blocks_pct 參數(shù)控制的,默認(rèn)為37,也就是 37% 。用圖表示大致如下: 數(shù)據(jù)在從磁盤被加載到緩存池的時(shí)候,首先是會(huì)被放在冷數(shù)據(jù)區(qū)的頭部,然后在一定時(shí)間之后,如果再次訪問(wèn)了這個(gè)數(shù)據(jù),那么這個(gè)數(shù)據(jù)所在的緩存頁(yè)對(duì)應(yīng)描述數(shù)據(jù)就會(huì)被放轉(zhuǎn)移到熱數(shù)據(jù)區(qū)鏈表的頭部。 那為什么說(shuō)是在一定的時(shí)間之后呢,假設(shè)某條數(shù)據(jù)剛被加載到緩存池中,然后緊接著又被訪問(wèn)了一次,這個(gè)時(shí)候假設(shè)就將其轉(zhuǎn)移到熱數(shù)據(jù)區(qū)鏈表的頭部,但是以后就再也不會(huì)被使用了,這樣子是不是就還是會(huì)存在之前的問(wèn)題呢? 所以 MySQL通過(guò)innodb_old_blocks_time來(lái)設(shè)置數(shù)據(jù)被加載到緩存池后的多少時(shí)間之后再次被訪問(wèn),才會(huì)將該數(shù)據(jù)轉(zhuǎn)移到熱數(shù)據(jù)區(qū)鏈表的頭部,該參數(shù)默認(rèn)是1000單位為:毫秒,也就是1秒之后,如果該數(shù)據(jù)又被訪問(wèn)了,那么這個(gè)時(shí)候才會(huì)將該數(shù)據(jù)從 LRU 鏈表的冷數(shù)據(jù)區(qū)轉(zhuǎn)移到熱數(shù)據(jù)區(qū)。 現(xiàn)在再回頭看下上面的問(wèn)題
再來(lái)思考下 Buffer Pool 內(nèi)存不夠的問(wèn)題 # Buffer Pool 內(nèi)存空間不夠使用了怎么辦?也就是說(shuō)沒(méi)有足夠使用的空閑的緩存頁(yè)了。1. 這個(gè)問(wèn)題在這個(gè)時(shí)候就顯得非常簡(jiǎn)單了,直接將鏈表冷數(shù)據(jù)區(qū)的尾節(jié)點(diǎn)的描述數(shù)據(jù)多對(duì)應(yīng)的緩存頁(yè)刷到磁盤即可。 但是這樣子還不是足夠完美,為什么這么說(shuō),剛剛我們一直在討論的是冷數(shù)據(jù)區(qū)的數(shù)據(jù)被訪問(wèn),然后在一定規(guī)則之下會(huì)被加載到熱數(shù)據(jù)鏈表的頭部,但是現(xiàn)在某個(gè)請(qǐng)求需要訪問(wèn)的數(shù)據(jù)就在熱數(shù)據(jù)區(qū),那是不是直接把該數(shù)據(jù)所在的緩存頁(yè)對(duì)應(yīng)的描述數(shù)據(jù)轉(zhuǎn)移到熱數(shù)據(jù)區(qū)鏈表頭部呢? 很顯然不是這樣子的,因?yàn)闊釘?shù)據(jù)區(qū)的數(shù)據(jù)本身就是會(huì)被頻繁訪問(wèn)的,這樣子如果每次訪問(wèn)都去移動(dòng)鏈表,勢(shì)必造成性能的下降(影響再小極端情況下也可能會(huì)不可控),所以 MySQL針對(duì)熱數(shù)據(jù)區(qū)的數(shù)據(jù)的轉(zhuǎn)移也有相關(guān)的規(guī)則。 該規(guī)則就是:如果被訪問(wèn)的數(shù)據(jù)所在的緩存頁(yè)在熱數(shù)據(jù)區(qū)的前25%,那么該緩存頁(yè)對(duì)應(yīng)的描述數(shù)據(jù)是不會(huì)被轉(zhuǎn)移到熱數(shù)據(jù)鏈表的頭部的,只有當(dāng)被訪問(wèn)的緩存頁(yè)對(duì)應(yīng)的描述數(shù)據(jù)在熱數(shù)據(jù)區(qū)鏈表的后75%,該緩存頁(yè)的描述數(shù)據(jù)才會(huì)被轉(zhuǎn)移到熱數(shù)據(jù)鏈表的頭部。 舉個(gè)例子來(lái)說(shuō),假設(shè)熱數(shù)據(jù)區(qū)有100個(gè)緩存頁(yè)(這里的緩存頁(yè)還是指的是緩存頁(yè)對(duì)應(yīng)的描述數(shù)據(jù),再?gòu)?qiáng)調(diào)下,鏈表中存放的是緩存頁(yè)的描述數(shù)據(jù),為了方便有時(shí)候會(huì)直接說(shuō)緩存頁(yè)。希望朋友們注意),當(dāng)被訪問(wèn)的緩存頁(yè)在前25個(gè)的時(shí)候,熱數(shù)據(jù)區(qū)的鏈表是不會(huì)有變化的,當(dāng)被訪問(wèn)的緩存頁(yè)在26~100(也就是數(shù)據(jù)在熱數(shù)據(jù)區(qū)鏈表的后75%里面)的時(shí)候,這個(gè)時(shí)候被訪問(wèn)的緩存頁(yè)才會(huì)被轉(zhuǎn)移到鏈表的頭部。 到此為止, MySQL對(duì)于LUR 鏈表的優(yōu)化就堪稱完美了。是不是看到這里瞬間感覺(jué)很多東西都明朗了,好了,對(duì)于 LRU 鏈表我們就討論到這里了。 10、Buffer Pool 中的鏈表小結(jié)
后臺(tái)線程將冷數(shù)據(jù)區(qū)的尾節(jié)點(diǎn)的描述數(shù)據(jù)對(duì)應(yīng)的緩存頁(yè)刷入磁盤文件中 11、Buffer Pool 的并發(fā)性能我們平時(shí)的系統(tǒng)絕對(duì)不可能每次只有一個(gè)請(qǐng)求來(lái)訪問(wèn)的,說(shuō)白了就是如果多個(gè)請(qǐng)求同時(shí)來(lái)執(zhí)行增刪改,那他們會(huì)并行的去操作 Buffer Pool 中的各種鏈表嗎?如果是并行的會(huì)不會(huì)有什么問(wèn)題。 實(shí)際上 MySQL在處理這個(gè)問(wèn)題的時(shí)候考慮得非常簡(jiǎn)單,就是: Buffer Pool 一次只能允許一個(gè)線程來(lái)操作,一次只有一個(gè)線程來(lái)執(zhí)行這一系列的操作,因?yàn)镸ySQL 為了保證數(shù)據(jù)的一致性,操作的時(shí)候必須緩存池加鎖,一次只能有一個(gè)線程獲取到鎖。 這個(gè)時(shí)候,大家這時(shí)候肯定滿腦子問(wèn)號(hào)。串行那還談什么效率?大家別忘記了,這一系列的操作都是在內(nèi)存中操作的,實(shí)際上這是一個(gè)瞬時(shí)的過(guò)程,在內(nèi)存中的操作基本是幾毫秒的甚至微妙級(jí)別的事情。 但是話又說(shuō)回來(lái),串行執(zhí)行再怎么快也是串行,雖然不是性能瓶頸,這還有更好的優(yōu)化辦法嗎?那肯定的 MySQL早就設(shè)計(jì)好了這些規(guī)則。那就是 Buffer Pool 是可以有多個(gè)的,可以通過(guò) MySQL的配置文件來(lái)配置,參數(shù)分別是: # Buffer Pool 的總大小innodb_buffer_pool_size=8589934592# Buffer Pool 的實(shí)例數(shù)(個(gè)數(shù))innodb_buffer_pool_instance=4 一般在生產(chǎn)環(huán)境中,在硬件不緊張的情況下,建議使用此策略。這個(gè)時(shí)候大家是不是又會(huì)有一個(gè)疑問(wèn)(如果沒(méi)有那說(shuō)明你沒(méi)認(rèn)真思考哦),大家應(yīng)該有這樣的疑問(wèn):
12、動(dòng)態(tài)調(diào)整 Buffer Pool 的大小到此為止,本文已經(jīng)詳細(xì)的介紹了 Buffer Pool 的內(nèi)存結(jié)構(gòu),它的數(shù)據(jù)是如何存放的,如何刷磁盤的,又是如何加載的,以什么樣的形式存在的等等知識(shí)點(diǎn),下面我們繼續(xù)挖掘,將 Buffer Pool 的相關(guān)知識(shí)點(diǎn)一次說(shuō)個(gè)夠。我們現(xiàn)在來(lái)討論下 Buffer Pool 的大小能否動(dòng)態(tài)調(diào)整。 假設(shè)我們現(xiàn)在的 Buffer Pool 的大小是 2GB大小,現(xiàn)在想將其擴(kuò)大到 4GB,現(xiàn)在說(shuō)一下如果真的要這么做,我們的 MySq 需要做哪些事情。首先 ,MySQL 需要向操作系統(tǒng)申請(qǐng)一塊大小為 4G 的連續(xù)的地址連續(xù)的內(nèi)存空間,然后將原來(lái)的 Buffer Pool 中的數(shù)據(jù)拷貝到新的 Buffer Pool 中。 這樣可能嗎?如果原來(lái)的是8G,擴(kuò)大到 16G,那這個(gè)將原來(lái)的數(shù)據(jù)復(fù)制到新的 Buffer Pool 中是不是極為耗時(shí)的,所以這樣的操作 MySQL必然是不支持的。但實(shí)際上這樣的需求是客觀存在的,那 MySQL是如何解決的呢? 為了處理這種情況,MySQL設(shè)計(jì)出 chunk (http 協(xié)議中也有使用到這個(gè)思想,所以我們會(huì)發(fā)現(xiàn)很多技術(shù)的優(yōu)秀思想都是在相互借鑒)機(jī)制來(lái)解決的 # 什么是chunk機(jī)制 chunk是 MySQL 設(shè)計(jì)的一種機(jī)制,這種機(jī)制的原理是將 Buffer Pool 拆分一個(gè)一個(gè)大小相等的 chunk 塊,每個(gè) chunk 默認(rèn)大小為 128M(可以通過(guò)參數(shù)innodb_buffer_pool_chunk_size 來(lái)調(diào)整大?。?,也就是說(shuō) Buffer Pool 是由一個(gè)個(gè)的chunk組成的 假設(shè) Buffer Pool 大小是2GB,而一個(gè)chunk大小默認(rèn)是128M,也就是說(shuō)一個(gè)2GB大小的 Buffer Pool 里面由16個(gè) chunk 組成,每個(gè)chunk中有自己的緩存頁(yè)和描述數(shù)據(jù),而 free 鏈表、flush 鏈表和 lru 鏈表是共享的 如果說(shuō)有多個(gè) Buffer Pool ,那就是這樣 說(shuō)到這里好像還是沒(méi)有說(shuō)到 MySQL到底是如何通過(guò) chunk 機(jī)制來(lái)調(diào)整大小的。實(shí)際上是這樣的,假設(shè)現(xiàn)在 Buffer Pool 有 2GB,里面有16個(gè)chunk,現(xiàn)在想要擴(kuò)大到 4GB,那么這個(gè)時(shí)候只需要新申請(qǐng)一個(gè)個(gè)的 chunk 就可以了。 這樣不但不需要申請(qǐng)一塊很大的連續(xù)的空間,更不需要將復(fù)制數(shù)據(jù)。這樣就能達(dá)到動(dòng)態(tài)調(diào)整大小了(不會(huì)還有人問(wèn):這只是擴(kuò)大,怎么縮小呢?gun)。不得不說(shuō) MySQL真機(jī)智。 13、生產(chǎn)環(huán)境如何設(shè)置 Buffer Pool 大小Buffer Pool 是不是越大越好,理論上是的。那如果一個(gè)機(jī)器內(nèi)存是16GB那分配給 Buffer Pool 15GB,這樣很顯然是不行的,因?yàn)椴僮飨到y(tǒng)要占內(nèi)存,你的機(jī)器上總會(huì)運(yùn)行其他的進(jìn)行的吧?那肯定也是需要占用內(nèi)存的。根據(jù)很多實(shí)際生產(chǎn)經(jīng)驗(yàn)得出得比較合理的大小是機(jī)器內(nèi)存大小的(50%~60%)。 最后一起來(lái)看看你的 INNODB 的相關(guān)參數(shù),命令是show engine innodb status
14、結(jié)束語(yǔ)本篇文章我們?cè)敿?xì)討論了 Buffer Pool 的內(nèi)存結(jié)構(gòu),從 free 鏈表到 lru 鏈表,從 Buffer Pool 到 chunk,從磁盤中加載一個(gè)數(shù)據(jù)頁(yè)到 Buffer Pool 到最后該數(shù)據(jù)頁(yè)又被刷回到磁盤中的一整個(gè)過(guò)程,他的每一步都做了什么。 我們一起討論完本文以后,是不是瞬間有種看透了 MySQL的感覺(jué),但是這個(gè)僅僅是前提,學(xué)習(xí)這些的目的是為了更好的理解 MySQL讓我們能夠在工作中更加游刃有余地使用他。因?yàn)橹挥性谥懒说讓釉淼那闆r下,才能熟悉他的工作原理,遇到問(wèn)題才能對(duì)癥下藥。 |
|
來(lái)自: Fengsq501u81r4 > 《計(jì)算機(jī)》