91超碰碰碰碰久久久久久综合_超碰av人澡人澡人澡人澡人掠_国产黄大片在线观看画质优化_txt小说免费全本

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

MySQL性能優化InnoDB buffer pool flush分析

發布時間:2021-11-10 11:38:45 來源:億速云 閱讀:237 作者:iii 欄目:MySQL數據庫

這篇文章主要講解了“MySQL性能優化InnoDB buffer pool flush分析”,文中的講解內容簡單清晰,易于學習與理解,下面請大家跟著小編的思路慢慢深入,一起來研究和學習“MySQL性能優化InnoDB buffer pool flush分析”吧!

背景

我們知道InnoDB使用buffer pool來緩存從磁盤讀取到內存的數據頁。buffer pool通常由數個內存塊加上一組控制結構體對象組成。內存塊的個數取決于buffer pool instance的個數,不過在5.7版本中開始默認以128M(可配置)的chunk單位分配內存塊,這樣做的目的是為了支持buffer pool的在線動態調整大小。

Buffer pool的每個內存塊通過mmap的方式分配內存,因此你會發現,在實例啟動時虛存很高,而物理內存很低。這些大片的內存塊又按照16KB劃分為多個frame,用于存儲數據頁。

雖然大多數情況下buffer pool是以16KB來存儲數據頁,但有一種例外:使用壓縮表時,需要在內存中同時存儲壓縮頁和解壓頁,對于壓縮頁,使用Binary buddy allocator算法來分配內存空間。例如我們讀入一個8KB的壓縮頁,就從buffer pool中取一個16KB的block,取其中8KB,剩下的8KB放到空閑鏈表上;如果緊跟著另外一個4KB的壓縮頁讀入內存,就可以從這8KB中分裂4KB,同時將剩下的4KB放到空閑鏈表上。

為了管理buffer pool,每個buffer pool instance 使用如下幾個鏈表來管理:

  • LRU鏈表包含所有讀入內存的數據頁;

  • Flush_list包含被修改過的臟頁;

  • unzip_LRU包含所有解壓頁;

  • Free list上存放當前空閑的block。


另外為了避免查詢數據頁時掃描LRU,還為每個buffer pool instance維護了一個page hash,通過space id 和page no可以直接找到對應的page。

一般情況下,當我們需要讀入一個Page時,首先根據space id 和page no找到對應的buffer pool instance。然后查詢page hash,如果page hash中沒有,則表示需要從磁盤讀取。在讀盤前首先我們需要為即將讀入內存的數據頁分配一個空閑的block。當free list上存在空閑的block時,可以直接從free list上摘取;如果沒有,就需要從unzip_lru 或者 lru上驅逐page。

這里需要遵循一定的原則(參考函數buf_LRU_scan_and_free_block , 5.7.5):

  1. 首先嘗試從unzip_lru上驅逐解壓頁;

  2. 如果沒有,再嘗試從Lru鏈表上驅逐Page;

  3. 如果還是無法從Lru上獲取到空閑block,用戶線程就會參與刷臟,嘗試做一次SINGLE PAGE FLUSH,單獨從Lru上刷掉一個臟頁,然后再重試。

Buffer pool中的page被修改后,不是立刻寫入磁盤,而是由后臺線程定時寫入,和大多數數據庫系統一樣,臟頁的寫盤遵循日志先行WAL原則,因此在每個block上都記錄了一個最近被修改時的Lsn,寫數據頁時需要確保當前寫入日志文件的redo不低于這個Lsn。

然而基于WAL原則的刷臟策略可能帶來一個問題:當數據庫的寫入負載過高時,產生redo log的速度極快,redo log可能很快到達同步checkpoint點。這時候需要進行刷臟來推進Lsn。由于這種行為是由用戶線程在檢查到redo log空間不夠時觸發,大量用戶線程將可能陷入到這段低效的邏輯中,產生一個明顯的性能拐點。


Page Cleaner線程

在MySQL5.6中,開啟了一個獨立的page cleaner線程來進行刷lru list 和flush list。默認每隔一秒運行一次,5.6版本里提供了一大堆的參數來控制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

這里我們不一一介紹,總的來說,如果你發現redo log推進的非常快,為了避免用戶線程陷入刷臟,可以通過調大innodb_io_capacity_max來解決,該參數限制了每秒刷新的臟頁上限,調大該值可以增加Page cleaner線程每秒的工作量。如果你發現你的系統中free list不足,總是需要驅逐臟頁來獲取空閑的block時,可以適當調大innodb_lru_scan_depth 。該參數表示從每個buffer pool instance的lru上掃描的深度,調大該值有助于多釋放些空閑頁,避免用戶線程去做single page flush。

為了提升擴展性和刷臟效率,在5.7.4版本里引入了多個page cleaner線程,從而達到并行刷臟的效果。目前Page cleaner并未和buffer pool綁定,其模型為一個協調線程 + 多個工作線程,協調線程本身也是工作線程。因此如果innodb_page_cleaners設置為4,那么就是一個協調線程,加3個工作線程,工作方式為生產者-消費者。工作隊列長度為buffer pool instance的個數,使用一個全局slot數組表示。

協調線程在決定了需要flush的page數和lsn_limit后,會設置slot數組,將其中每個slot的狀態設置為PAGE_CLEANER_STATE_REQUESTED, 并設置目標page數及lsn_limit,然后喚醒工作線程 (pc_request)

工作線程被喚醒后,從slot數組中取一個未被占用的slot,修改其狀態,表示已被調度,然后對該slot所對應的buffer pool instance進行操作。直到所有的slot都被消費完后,才進入下一輪。通過這種方式,多個page cleaner線程實現了并發flush buffer pool,從而提升flush dirty page/lru的效率。


MySQL5.7的InnoDB flush策略優化

在之前版本中,因為可能同時有多個線程操作buffer pool刷page (在刷臟時會釋放buffer pool mutex),每次刷完一個page后需要回溯到鏈表尾部,使得掃描bp鏈表的時間復雜度最差為O(N*N)。

在5.6版本中針對Flush list的掃描做了一定的修復,使用一個指針來記錄當前正在flush的page,待flush操作完成后,再看一下這個指針有沒有被別的線程修改掉,如果被修改了,就回溯到鏈表尾部,否則無需回溯。但這個修復并不完整,在最差的情況下,時間復雜度依舊不理想。

因此在5.7版本中對這個問題進行了徹底的修復,使用多個名為hazard pointer的指針,在需要掃描LIST時,存儲下一個即將掃描的目標page,根據不同的目的分為幾類:

  • flush_hp: 用作批量刷FLUSH LIST

  • lru_hp: 用作批量刷LRU LIST

  • lru_scan_itr: 用于從LRU鏈表上驅逐一個可替換的page,總是從上一次掃描結束的位置開始,而不是LRU尾部

  • single_scan_itr: 當buffer pool中沒有空閑block時,用戶線程會從FLUSH LIST上單獨驅逐一個可替換的page 或者 flush一個臟頁,總是從上一次掃描結束的位置開始,而不是LRU尾部。

后兩類的hp都是由用戶線程在嘗試獲取空閑block時調用,只有在推進到某個buf_page_t::old被設置成true的page (大約從Lru鏈表尾部起至總長度的八分之三位置的page)時, 再將指針重置到Lru尾部。

這些指針在初始化buffer pool時分配,每個buffer pool instance都擁有自己的hp指針。當某個線程對buffer pool中的page進行操作時,例如需要從LRU中移除Page時,如果當前的page被設置為hp,就要將hp更新為當前Page的前一個page。當完成當前page的flush操作后,直接使用hp中存儲的page指針進行下一輪flush。


社區優化

一如既往的,Percona Server在5.6版本中針對buffer pool flush做了不少的優化,主要的修改包括如下幾點:

  • 優化刷LRU流程buf_flush_LRU_tail
    該函數由page cleaner線程調用。

    • 原生的邏輯:依次flush 每個buffer pool instance,每次掃描的深度通過參數innodb_lru_scan_depth來配置。而在每個instance內,又分成多個chunk來調用;

    • 修改后的邏輯為:每次flush一個buffer pool的LRU時,只刷一個chunk,然后再下一個instance,刷完所有instnace后,再回到前面再刷一個chunk。簡而言之,把集中的flush操作進行了分散,其目的是分散壓力,避免對某個instance的集中操作,給予其他線程更多訪問buffer pool的機會。

  • 允許設定刷LRU/FLUSH LIST的超時時間,防止flush操作時間過長導致別的線程(例如嘗試做single page flush的用戶線程)stall住;當到達超時時間時,page cleaner線程退出flush。

  • 避免用戶線程參與刷buffer pool
    當用戶線程參與刷buffer pool時,由于線程數的不可控,將產生嚴重的競爭開銷,例如free list不足時做single page flush,以及在redo空間不足時,做dirty page flush,都會嚴重影響性能。Percona Server允許選擇讓page cleaner線程來做這些工作,用戶線程只需要等待即可。出于效率考慮,用戶還可以設置page cleaner線程的cpu調度優先級。
    另外在Page cleaner線程經過優化后,可以知道系統當前處于同步刷新狀態,可以去做更激烈的刷臟(furious flush),用戶線程參與到其中,可能只會起到反作用。

  • 允許設置page cleaner線程,purge線程,io線程,master線程的CPU調度優先級,并優先獲得InnoDB的mutex。

    • 使用新的獨立后臺線程來刷buffer pool的LRU鏈表,將這部分工作負擔從page cleaner線程剝離。
      實際上就是直接轉移刷LRU的代碼到獨立線程了。從之前Percona的版本來看,都是在不斷的強化后臺線程,讓用戶線程少參與到刷臟/checkpoint這類耗時操作中。

感謝各位的閱讀,以上就是“MySQL性能優化InnoDB buffer pool flush分析”的內容了,經過本文的學習后,相信大家對MySQL性能優化InnoDB buffer pool flush分析這一問題有了更深刻的體會,具體使用情況還需要大家實踐驗證。這里是億速云,小編將為大家推送更多相關知識點的文章,歡迎關注!

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

田东县| 博罗县| 麻江县| 富裕县| 永修县| 玉环县| 五原县| 和田县| 鲁甸县| 鹰潭市| 山阴县| 香港| 雷山县| 江安县| 鲁甸县| 佛学| 敦煌市| 竹溪县| 新绛县| 新竹市| 巩留县| 工布江达县| 文登市| 西乡县| 霍山县| 茶陵县| 汤阴县| 张家界市| 来宾市| 万州区| 洛宁县| 孟津县| 友谊县| 林口县| 丰宁| 壤塘县| 昌乐县| 黄梅县| 宣恩县| 抚宁县| 西林县|