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