這篇文章主要講解了“MySQL性能優(yōu)化InnoDB buffer pool flush分析”,文中的講解內(nèi)容簡單清晰,易于學(xué)習(xí)與理解,下面請大家跟著小編的思路慢慢深入,一起來研究和學(xué)習(xí)“MySQL性能優(yōu)化InnoDB buffer pool flush分析”吧!
十多年的陽谷網(wǎng)站建設(shè)經(jīng)驗,針對設(shè)計、前端、開發(fā)、售后、文案、推廣等六對一服務(wù),響應(yīng)快,48小時及時工作處理。全網(wǎng)營銷推廣的優(yōu)勢是能夠根據(jù)用戶設(shè)備顯示端的尺寸不同,自動調(diào)整陽谷建站的顯示方式,使網(wǎng)站能夠適用不同顯示終端,在瀏覽器中調(diào)整網(wǎng)站的寬度,無論在任何一種瀏覽器上瀏覽網(wǎng)站,都能展現(xiàn)優(yōu)雅布局與設(shè)計,從而大程度地提升瀏覽體驗。成都創(chuàng)新互聯(lián)從事“陽谷網(wǎng)站設(shè)計”,“陽谷網(wǎng)站推廣”以來,每個客戶項目都認真落實執(zhí)行。
背景
我們知道InnoDB使用buffer pool來緩存從磁盤讀取到內(nèi)存的數(shù)據(jù)頁。buffer pool通常由數(shù)個內(nèi)存塊加上一組控制結(jié)構(gòu)體對象組成。內(nèi)存塊的個數(shù)取決于buffer pool instance的個數(shù),不過在5.7版本中開始默認以128M(可配置)的chunk單位分配內(nèi)存塊,這樣做的目的是為了支持buffer pool的在線動態(tài)調(diào)整大小。
Buffer pool的每個內(nèi)存塊通過mmap的方式分配內(nèi)存,因此你會發(fā)現(xiàn),在實例啟動時虛存很高,而物理內(nèi)存很低。這些大片的內(nèi)存塊又按照16KB劃分為多個frame,用于存儲數(shù)據(jù)頁。
雖然大多數(shù)情況下buffer pool是以16KB來存儲數(shù)據(jù)頁,但有一種例外:使用壓縮表時,需要在內(nèi)存中同時存儲壓縮頁和解壓頁,對于壓縮頁,使用Binary buddy allocator算法來分配內(nèi)存空間。例如我們讀入一個8KB的壓縮頁,就從buffer pool中取一個16KB的block,取其中8KB,剩下的8KB放到空閑鏈表上;如果緊跟著另外一個4KB的壓縮頁讀入內(nèi)存,就可以從這8KB中分裂4KB,同時將剩下的4KB放到空閑鏈表上。
為了管理buffer pool,每個buffer pool instance 使用如下幾個鏈表來管理:
LRU鏈表包含所有讀入內(nèi)存的數(shù)據(jù)頁;
Flush_list包含被修改過的臟頁;
unzip_LRU包含所有解壓頁;
Free list上存放當(dāng)前空閑的block。
另外為了避免查詢數(shù)據(jù)頁時掃描LRU,還為每個buffer pool instance維護了一個page hash,通過space id 和page no可以直接找到對應(yīng)的page。
一般情況下,當(dāng)我們需要讀入一個Page時,首先根據(jù)space id 和page no找到對應(yīng)的buffer pool instance。然后查詢page hash,如果page hash中沒有,則表示需要從磁盤讀取。在讀盤前首先我們需要為即將讀入內(nèi)存的數(shù)據(jù)頁分配一個空閑的block。當(dāng)free list上存在空閑的block時,可以直接從free list上摘?。蝗绻麤]有,就需要從unzip_lru 或者 lru上驅(qū)逐page。
這里需要遵循一定的原則(參考函數(shù)buf_LRU_scan_and_free_block , 5.7.5):
首先嘗試從unzip_lru上驅(qū)逐解壓頁;
如果沒有,再嘗試從Lru鏈表上驅(qū)逐Page;
如果還是無法從Lru上獲取到空閑block,用戶線程就會參與刷臟,嘗試做一次SINGLE PAGE FLUSH,單獨從Lru上刷掉一個臟頁,然后再重試。
Buffer pool中的page被修改后,不是立刻寫入磁盤,而是由后臺線程定時寫入,和大多數(shù)數(shù)據(jù)庫系統(tǒng)一樣,臟頁的寫盤遵循日志先行WAL原則,因此在每個block上都記錄了一個最近被修改時的Lsn,寫數(shù)據(jù)頁時需要確保當(dāng)前寫入日志文件的redo不低于這個Lsn。
然而基于WAL原則的刷臟策略可能帶來一個問題:當(dāng)數(shù)據(jù)庫的寫入負載過高時,產(chǎn)生redo log的速度極快,redo log可能很快到達同步checkpoint點。這時候需要進行刷臟來推進Lsn。由于這種行為是由用戶線程在檢查到redo log空間不夠時觸發(fā),大量用戶線程將可能陷入到這段低效的邏輯中,產(chǎn)生一個明顯的性能拐點。
Page Cleaner線程
在MySQL5.6中,開啟了一個獨立的page cleaner線程來進行刷lru list 和flush list。默認每隔一秒運行一次,5.6版本里提供了一大堆的參數(shù)來控制page cleaner的flush行為,包括:
innodb_adaptive_flushing_lwm, innodb_max_dirty_pages_pct_lwm innodb_flushing_avg_loops innodb_io_capacity_max innodb_lru_scan_depth
這里我們不一一介紹,總的來說,如果你發(fā)現(xiàn)redo log推進的非??欤瑸榱吮苊庥脩艟€程陷入刷臟,可以通過調(diào)大innodb_io_capacity_max來解決,該參數(shù)限制了每秒刷新的臟頁上限,調(diào)大該值可以增加Page cleaner線程每秒的工作量。如果你發(fā)現(xiàn)你的系統(tǒng)中free list不足,總是需要驅(qū)逐臟頁來獲取空閑的block時,可以適當(dāng)調(diào)大innodb_lru_scan_depth 。該參數(shù)表示從每個buffer pool instance的lru上掃描的深度,調(diào)大該值有助于多釋放些空閑頁,避免用戶線程去做single page flush。
為了提升擴展性和刷臟效率,在5.7.4版本里引入了多個page cleaner線程,從而達到并行刷臟的效果。目前Page cleaner并未和buffer pool綁定,其模型為一個協(xié)調(diào)線程 + 多個工作線程,協(xié)調(diào)線程本身也是工作線程。因此如果innodb_page_cleaners設(shè)置為4,那么就是一個協(xié)調(diào)線程,加3個工作線程,工作方式為生產(chǎn)者-消費者。工作隊列長度為buffer pool instance的個數(shù),使用一個全局slot數(shù)組表示。
協(xié)調(diào)線程在決定了需要flush的page數(shù)和lsn_limit后,會設(shè)置slot數(shù)組,將其中每個slot的狀態(tài)設(shè)置為PAGE_CLEANER_STATE_REQUESTED, 并設(shè)置目標(biāo)page數(shù)及l(fā)sn_limit,然后喚醒工作線程 (pc_request)
工作線程被喚醒后,從slot數(shù)組中取一個未被占用的slot,修改其狀態(tài),表示已被調(diào)度,然后對該slot所對應(yīng)的buffer pool instance進行操作。直到所有的slot都被消費完后,才進入下一輪。通過這種方式,多個page cleaner線程實現(xiàn)了并發(fā)flush buffer pool,從而提升flush dirty page/lru的效率。
MySQL5.7的InnoDB flush策略優(yōu)化
在之前版本中,因為可能同時有多個線程操作buffer pool刷page (在刷臟時會釋放buffer pool mutex),每次刷完一個page后需要回溯到鏈表尾部,使得掃描bp鏈表的時間復(fù)雜度最差為O(N*N)。
在5.6版本中針對Flush list的掃描做了一定的修復(fù),使用一個指針來記錄當(dāng)前正在flush的page,待flush操作完成后,再看一下這個指針有沒有被別的線程修改掉,如果被修改了,就回溯到鏈表尾部,否則無需回溯。但這個修復(fù)并不完整,在最差的情況下,時間復(fù)雜度依舊不理想。
因此在5.7版本中對這個問題進行了徹底的修復(fù),使用多個名為hazard pointer的指針,在需要掃描LIST時,存儲下一個即將掃描的目標(biāo)page,根據(jù)不同的目的分為幾類:
flush_hp: 用作批量刷FLUSH LIST
lru_hp: 用作批量刷LRU LIST
lru_scan_itr: 用于從LRU鏈表上驅(qū)逐一個可替換的page,總是從上一次掃描結(jié)束的位置開始,而不是LRU尾部
single_scan_itr: 當(dāng)buffer pool中沒有空閑block時,用戶線程會從FLUSH LIST上單獨驅(qū)逐一個可替換的page 或者 flush一個臟頁,總是從上一次掃描結(jié)束的位置開始,而不是LRU尾部。
后兩類的hp都是由用戶線程在嘗試獲取空閑block時調(diào)用,只有在推進到某個buf_page_t::old被設(shè)置成true的page (大約從Lru鏈表尾部起至總長度的八分之三位置的page)時, 再將指針重置到Lru尾部。
這些指針在初始化buffer pool時分配,每個buffer pool instance都擁有自己的hp指針。當(dāng)某個線程對buffer pool中的page進行操作時,例如需要從LRU中移除Page時,如果當(dāng)前的page被設(shè)置為hp,就要將hp更新為當(dāng)前Page的前一個page。當(dāng)完成當(dāng)前page的flush操作后,直接使用hp中存儲的page指針進行下一輪flush。
社區(qū)優(yōu)化
一如既往的,Percona Server在5.6版本中針對buffer pool flush做了不少的優(yōu)化,主要的修改包括如下幾點:
優(yōu)化刷LRU流程buf_flush_LRU_tail
該函數(shù)由page cleaner線程調(diào)用。
原生的邏輯:依次flush 每個buffer pool instance,每次掃描的深度通過參數(shù)innodb_lru_scan_depth來配置。而在每個instance內(nèi),又分成多個chunk來調(diào)用;
修改后的邏輯為:每次flush一個buffer pool的LRU時,只刷一個chunk,然后再下一個instance,刷完所有instnace后,再回到前面再刷一個chunk。簡而言之,把集中的flush操作進行了分散,其目的是分散壓力,避免對某個instance的集中操作,給予其他線程更多訪問buffer pool的機會。
允許設(shè)定刷LRU/FLUSH LIST的超時時間,防止flush操作時間過長導(dǎo)致別的線程(例如嘗試做single page flush的用戶線程)stall?。划?dāng)?shù)竭_超時時間時,page cleaner線程退出flush。
避免用戶線程參與刷buffer pool
當(dāng)用戶線程參與刷buffer pool時,由于線程數(shù)的不可控,將產(chǎn)生嚴(yán)重的競爭開銷,例如free list不足時做single page flush,以及在redo空間不足時,做dirty page flush,都會嚴(yán)重影響性能。Percona Server允許選擇讓page cleaner線程來做這些工作,用戶線程只需要等待即可。出于效率考慮,用戶還可以設(shè)置page cleaner線程的cpu調(diào)度優(yōu)先級。
另外在Page cleaner線程經(jīng)過優(yōu)化后,可以知道系統(tǒng)當(dāng)前處于同步刷新狀態(tài),可以去做更激烈的刷臟(furious flush),用戶線程參與到其中,可能只會起到反作用。
允許設(shè)置page cleaner線程,purge線程,io線程,master線程的CPU調(diào)度優(yōu)先級,并優(yōu)先獲得InnoDB的mutex。
使用新的獨立后臺線程來刷buffer pool的LRU鏈表,將這部分工作負擔(dān)從page cleaner線程剝離。
實際上就是直接轉(zhuǎn)移刷LRU的代碼到獨立線程了。從之前Percona的版本來看,都是在不斷的強化后臺線程,讓用戶線程少參與到刷臟/checkpoint這類耗時操作中。
感謝各位的閱讀,以上就是“MySQL性能優(yōu)化InnoDB buffer pool flush分析”的內(nèi)容了,經(jīng)過本文的學(xué)習(xí)后,相信大家對MySQL性能優(yōu)化InnoDB buffer pool flush分析這一問題有了更深刻的體會,具體使用情況還需要大家實踐驗證。這里是創(chuàng)新互聯(lián),小編將為大家推送更多相關(guān)知識點的文章,歡迎關(guān)注!
當(dāng)前文章:MySQL性能優(yōu)化InnoDBbufferpoolflush分析
瀏覽路徑:http://jinyejixie.com/article46/pgepeg.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供網(wǎng)站收錄、網(wǎng)站制作、靜態(tài)網(wǎng)站、企業(yè)建站、定制網(wǎng)站、外貿(mào)網(wǎng)站建設(shè)
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時需注明來源: 創(chuàng)新互聯(lián)