吴忠躺衫网络科技有限公司

0
  • 聊天消息
  • 系統(tǒng)消息
  • 評(píng)論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會(huì)員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識(shí)你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

在InnoDB如何選擇從LRU_list

數(shù)據(jù)庫(kù)和存儲(chǔ) ? 來(lái)源:數(shù)據(jù)庫(kù)和存儲(chǔ) ? 2023-05-29 10:59 ? 次閱讀

在InnoDB 如何選擇從LRU_list 刷臟還是從flush_list 刷臟, 本質(zhì)問(wèn)題就是page replacement policies

fast path: 從LRU_list 刷臟, 依據(jù)的是哪些page 最老被訪問(wèn)過(guò)來(lái)排序, 由于有些 page 沒(méi)有被修改過(guò), 那么釋放page 可以無(wú)需IO, 可以快速獲得free page. 但是也有page 被修改, 那么就需要進(jìn)行IO 了.

slow path: 從flush list 刷臟, 是按照 oldest_modification lsn來(lái)排序, 并且flush list 上面的page 都是臟頁(yè), 獲得free page 必須進(jìn)行一次IO, 但是刷臟下去以后checkpoint 就可以推進(jìn), redo 空間就可以釋放, crash recovery 就更快.

另外由于page 的newest_modification lsn 有可能一直在漲, 需要等到redo log log_write_up_to lsn 以后該page 才可以刷下去, 具體函數(shù) buf_flush_write_block_low() 里面, 因此對(duì)比LRU list 上刷臟獲得free page 肯定慢很多.

page 從flush_list 上面刷臟以后是不會(huì)從lru list 上面刪除, 也就不會(huì)釋放, 需要等下一次lru list 刷臟的時(shí)候再進(jìn)行釋放.

所以InnoDB 是完全按照l(shuí)ru list 的順序去獲得free page, 但是不是按照l(shuí)ru list 的順序進(jìn)行刷臟.

我理解官方單一線程刷flush list 和 lru list 問(wèn)題是把獲得free page 和刷臟操作放在同一個(gè)線程中執(zhí)行了, 導(dǎo)致在刷臟任務(wù)比較重的時(shí)候無(wú)法獲得free page

buffer pool 對(duì)IO 優(yōu)化很重要的一個(gè)作用是寫入聚合, 對(duì)一個(gè)Page 多次修改合并成一個(gè)IO 寫入. 所以對(duì)于系統(tǒng)而言保留一定的臟頁(yè)率對(duì)性能是有收益的.

理論上如果只有不考慮redo log checkpoint 及時(shí)推進(jìn), 那么最好的推進(jìn)策略一定是在LRU list 上面一直刷臟, 因?yàn)檫@樣才充分發(fā)揮buffer pool IO 聚合以及LRU 策略.

我們提出一個(gè)足夠簡(jiǎn)單的模型.

如果寫入redo 速度不變, 那么生成page 速度不變, 如果刷臟能力極其快, 那么理論上LRU_scan_depth 的深度設(shè)置成用戶每秒最大的page IO 生成能力即可, 那么系統(tǒng)最好的狀態(tài) page dirty pct = (buffer pool size - LRU_scan_depth page) / buffer pool size

進(jìn)一步添加約束, 刷臟能力不如生成page 速度, 那么隨著用戶的寫入臟頁(yè)的百分比肯定最后會(huì)100%, 所以這個(gè)時(shí)候必然需要限制用戶的寫入速度, 使得 page 生成速度 < 刷臟速度, 那么臟頁(yè)才可以穩(wěn)定下來(lái).

在dirty_page_pct < trigger_slow_user_written 的情況下, 不阻止用戶寫入

在dirty_page_pct > trigger_slow_user_written 的情況下, 那么需要阻止用戶寫入了.

trigger_slow_user_written 設(shè)置的越低, 那么越早開(kāi)始阻止用戶的寫入, 那么能夠容忍偶爾用戶寫入峰值的時(shí)間越長(zhǎng).

進(jìn)一步增加flush list 的考慮, 考慮從flush list 上面刷臟其實(shí)降低刷臟能力.

比如redo 寫入10MB, 生成page 100MB, 如果修改的是完全不同page, 也就是buffer pool 沒(méi)有起到IO 聚合作用, 那么刷flush list 和 lru list 是一樣的, 但是實(shí)際因?yàn)橛锌赡苡行﹑age 是重復(fù)修改, 理論上LRU list 上刷臟效率> flush list 刷臟效率.

所以在引入了flush list 以后, 考慮的策略是:

在dirty_page_pct < trigger_flush_list 的情況下, 應(yīng)該完全從lru list 上面刷臟

在dirty_page_pct > trigger_flush_list 的情況下, 那么優(yōu)先從flush list 上面刷臟了

在dirty_page_pct > trigger_slow_user_written 的情況下, 那么需要阻止用戶寫入了.

那么增加考慮flush list 以后, 實(shí)際刷臟策略是 lru list + flush list, 實(shí)際刷臟效率進(jìn)一步降低, 那么就需要更早的對(duì)用戶的寫入進(jìn)行阻止.

在PolarDB 上, 由于需要考慮ro 節(jié)點(diǎn)盡可能可以將parse buffer 讓出, 會(huì)觸發(fā)rw 節(jié)點(diǎn)盡快刷臟, 降低了buffer pool IO聚合作用, 那么會(huì)進(jìn)一步降低了刷臟的效率.

ca15b19a-fdc8-11ed-90ce-dac502259ad0.png

那么這里臟頁(yè)率多少合適觸發(fā)阻止用戶寫入呢?

觸發(fā)trigger_slow_user_writtern 還需要考慮用戶需要使用free page 場(chǎng)景. 如果臟頁(yè)百分比特別低, 那么容易在LRU list 上獲得free page, 如果臟頁(yè)百分比高, 那么就不容易獲得free page.

在我們上述的模型里面只考慮到了用戶的寫入和刷臟之間的關(guān)系, 并沒(méi)有考慮到在有一定臟頁(yè)比例的情況, 用戶讀取請(qǐng)求如何獲得 free page 的問(wèn)題.

如果沒(méi)有合理實(shí)現(xiàn), 那么用戶請(qǐng)求讀取的時(shí)候需要遍歷大量dirty page 才可以找到空閑頁(yè), 影響用戶訪問(wèn)性能.

另外這里為了找free page 性能, 也不能把dirty_page_pct 設(shè)置的過(guò)高.

這里的topic 是, 在有一定臟頁(yè)的情況下, 如何合理組織page 使得能夠高效獲得free page.

目前InnoDB 的做法是:

分兩種情況:

  1. 在系統(tǒng)正常運(yùn)行的過(guò)程中, 后臺(tái)page cleaner 線程不斷通過(guò) buf_flush_LRU_list_batch() 函數(shù)對(duì)LRU list 上面old page 進(jìn)行回收, 添加到free list 里面.

  2. 在用戶發(fā)現(xiàn)free list 沒(méi)有free page 以后, 通過(guò)buf_LRU_get_free_block() 主動(dòng)從LRU list 上面獲取free page

還有一個(gè)問(wèn)題: 用戶線程從LRU_list 上獲得free page 需要持有LRU_list_mutex, 但是后臺(tái)的page cleaner 線程也同樣需要持有LRU_list_mutex 進(jìn)行清理操作, 這里就會(huì)有一個(gè)爭(zhēng)搶.

并且 LRU_list 釋放block 的過(guò)程并不是一直持有LRU_list_mutex, 是對(duì)于每一個(gè)block 而言, 持有LRU_list_mutex, 釋放 mutex, 進(jìn)行具體的IO 操作, IO操作完又加上LRU_list_mutex.

為什么這樣操作, 而不是一直持有LRU_list_mutex 釋放完指定number block 以后, 再釋放LRU_list_mutex?

這樣的話用戶線程就獲得不到LRU_list_mutex 了, 那么就會(huì)導(dǎo)致用戶請(qǐng)求卡主, 但是這里用戶請(qǐng)求大部分是從free list 上面獲得page, 只有free page 上面沒(méi)有page 的時(shí)候才會(huì)從LRU_list 上面去獲得page.

具體代碼 buf_page_free_stale => buf_LRU_free_page() 在進(jìn)行IO 操作之前, 釋放 LRU_list_mutex, 結(jié)束之后退出buf_page_free_stale() 重新加LRU_list_mutex.

ca418f54-fdc8-11ed-90ce-dac502259ad0.png

另外, 這里嘗試釋放某一個(gè)block 的時(shí)候是用mutex_enter_nowait(pthread_mutex_trylock), 不會(huì)去等待, 也就是如果某一個(gè)page_block 被別人使用, 是不會(huì)去釋放這個(gè)block 的. 也可以理解, 這個(gè)mutex被人持有, 那么大概率這個(gè)block 正在使用, 那么可能又是一個(gè)新的page 了, 不需要被釋放

1. buf_flush_LRU_list_batch()

后臺(tái)對(duì)LRU_list 的批量刷臟只會(huì)只會(huì)掃描 srv_LRU_scan_depth 深度, 在LRU list 末尾 srv_LRU_scan_depth 長(zhǎng)度內(nèi), 遇到的page 如果是dirty page, 那么就執(zhí)行 buf_flush_page_and_try_neighbors() 進(jìn)行刷臟操作, 如果是non-dirty page, 那么就直接踢除就可以.

如果page non-dirty page, 在buf_flush_ready_for_replace() 函數(shù)中進(jìn)行判斷, 然后執(zhí)行 buf_LRU_free_page() 邏輯

這里判斷一個(gè)page 是否能夠被replace, 也就是被釋放的方法
如果這個(gè)page 是被寫過(guò), 那么oldest_modification == 0, 表示這個(gè)page已經(jīng)被flush 到磁盤了.
bpage->buf_fix_count 表示的是記錄這個(gè)bpage 被引用次數(shù), 每次訪問(wèn)bpage,都對(duì)引用計(jì)數(shù)buf_fix_count + 1, 釋放的時(shí)候 -1. 也就是這個(gè)bpage 沒(méi)有其他人訪問(wèn)以后,才可以被replace
并且這個(gè)page 的io_fix 狀態(tài)是 BUF_IO_NONE, 表示的是page 要從LRU list 中刪除, 在page 用完以后, 都會(huì)設(shè)置成 BUF_IO_NONE.如果是BUF_IO_READ, BUF_IO_WRITE 表示的是這個(gè)page 要從底下文件中讀取或者寫入, 肯定還在使用, 所以不能被replace

如果可以replace, 則執(zhí)行 buf_LRU_free_page()

如果page 是dirty page, 在 buf_flush_ready_for_flush() 中判斷, 最后執(zhí)行buf_flush_page() 邏輯.

這個(gè)page 的oldest_modification != 0, 表示這個(gè)page 肯定已經(jīng)被修改過(guò)了, 并且 io_fix == NONE, 不然這個(gè)page 可能正要被read/write

如果可以flush, 則執(zhí)行 buf_flush_page()

這里掃描LRU list 末尾 srv_LRU_scan_depth長(zhǎng)度, 如果末尾的page 都是dirty page, 那么獲取free page 就不夠高效.

2. 具體用戶請(qǐng)求獲得free page 的策略方法在函數(shù) buf_LRU_get_free_block() 中. 具體策略

cad0c6d8-fdc8-11ed-90ce-dac502259ad0.jpg

和page cleaner 刷臟區(qū)別的地方在于 第2次scan 的時(shí)候會(huì)掃描整個(gè)LRU list 去獲得free page, 在第2次scan 以后, 會(huì)sleep 10ms, 并且超過(guò)20次以后會(huì)打印沒(méi)有free page 信息.

為什么不直接從LRU list 上面拿出一個(gè)被modify 并且未執(zhí)行flush 的page 執(zhí)行flush 呢?

因?yàn)樵贚RU list 上是按照access_time 排序的, 所有有可能page 被修改以后, 又有讀, 因?yàn)檫@個(gè)page 被排在了很前面. 但是有可能這個(gè)page 很早被修改, 但是一直沒(méi)有讀, 反而排在了后面了, 因此從flush_list 里面找page 進(jìn)行flush 是更靠譜的, 保證flush 的是最早修改過(guò)的page

那么什么時(shí)候會(huì)從flush_list 上面去執(zhí)行flush page 操作呢?

在系統(tǒng)正常運(yùn)行的過(guò)程中, 就不斷會(huì)有page clean 線程對(duì)page 執(zhí)行 flush 操作, 這樣可以觸發(fā)用戶從LRU list 里面找page 的時(shí)候, 只需要replace 操作, 不需要flush single page 操作, 因?yàn)閒lush single page 操作如果觸發(fā), 對(duì)用戶的請(qǐng)求性能影響很大.

所以在page cleaner thread 執(zhí)行flush 操作以后, 在寫IO 完成以后, 是否會(huì)把page 同時(shí)從flush_list, LRU list 同時(shí)刪除, 還是只是將oldest_modification lsn 設(shè)置成0 就可以了?

這里分兩種場(chǎng)景考慮:

  1. 如果這個(gè)page 是從flush_list 上面寫IO 完成, 那么就不需要從flush_list上面刪除, 因?yàn)閺膄lush list 上面刪除要完成的操作是刷臟,既然只是為了刷臟, 那么就沒(méi)必要讓他從lru list 上面刪除, 有可能這個(gè)page 被刷臟了, 還是一個(gè)熱page 是需要訪問(wèn)的

  2. 如果這個(gè)page 是從lru_list 上面寫IO 完成, 那就需要從lru list 上面刪除

    原因: 從lru_list 上面刪除的page 肯定說(shuō)明這個(gè)page 不是hot page 了,更大的原因可能是buffer pool 空間不夠, 需要從lru list 上面淘汰一些page了, 既然這些page 是要從lru list 上面淘汰的, 那么肯定就需要從LRU list 上面移除

具體代碼在buf_page_io_complete() 中

Q&A

另外在刷臟里面最大的一個(gè)問(wèn)題是InnoDB 刷臟過(guò)程是需要持有page sx lock, 有兩個(gè)地方導(dǎo)致可能持有page latch 時(shí)間過(guò)長(zhǎng)

  1. 進(jìn)行刷臟的時(shí)候有可能page newest_modification lsn 比較大, 那么刷臟的時(shí)候需要等redo log 已經(jīng)寫入到 log_write_up_to 到這個(gè)lsn 才可以, 那么加鎖的時(shí)間就大大加長(zhǎng)了

  2. 進(jìn)行刷臟的時(shí)候持有l(wèi)atch 的時(shí)間是加入到simulated AIO 隊(duì)列就算上了, 因此整體持有l(wèi)atch 的時(shí)間是 在simulated AIO 上等待的時(shí)間 + IO 時(shí)間

通過(guò)Single Page flush 或者通過(guò)用戶Thread wait LRU manager thread 獲得空閑Page區(qū)別?

通過(guò)Single Page flush 或者通過(guò)用戶Thread wait LRU manager thread 獲得空閑Page, 解決的都是在臟頁(yè)百分比較高情況下, 獲得free page 的工程上的方法, 只不過(guò)Single page flush 在用戶線程比較多的情況下, 非常多個(gè)用戶線程去搶占同一個(gè)LRU list mutex, 而通過(guò)wait LRU manager thread 的方法, 因?yàn)閠hread number 有限, 不會(huì)有過(guò)多的線程搶占同一個(gè)LRU list mutex, 所以在工程上會(huì)更好一些.

但是其實(shí)用戶用Single Page flush 和引入不引入LRU manager thread 其實(shí)是無(wú)關(guān)的.


審核編輯 :李倩


聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場(chǎng)。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問(wèn)題,請(qǐng)聯(lián)系本站處理。 舉報(bào)投訴
  • 磁盤
    +關(guān)注

    關(guān)注

    1

    文章

    380

    瀏覽量

    25276
  • 模型
    +關(guān)注

    關(guān)注

    1

    文章

    3305

    瀏覽量

    49221
  • 線程
    +關(guān)注

    關(guān)注

    0

    文章

    505

    瀏覽量

    19758

原文標(biāo)題:InnoDB page replacement policies

文章出處:【微信號(hào):inf_storage,微信公眾號(hào):數(shù)據(jù)庫(kù)和存儲(chǔ)】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。

收藏 人收藏

    評(píng)論

    相關(guān)推薦

    LRU緩存模塊最佳實(shí)踐

    LRU(Least Recently Used)是一種緩存替換算法,它的核心思想是當(dāng)緩存滿時(shí),替換最近最少使用的數(shù)據(jù)。實(shí)際應(yīng)用中,LRU算法被廣泛應(yīng)用于緩存、頁(yè)面置換等領(lǐng)域。Rust語(yǔ)言提供了一個(gè)
    的頭像 發(fā)表于 09-30 16:47 ?956次閱讀

    Redis的LRU實(shí)現(xiàn)和應(yīng)用

    在編程中,計(jì)數(shù)器是一種基本但強(qiáng)大的工具,用于跟蹤和管理數(shù)據(jù)和資源。本文將深入探討不同類型的計(jì)數(shù)器的應(yīng)用,Redis的LRU(最近最少使用)緩存淘汰算法的實(shí)現(xiàn),到如何在內(nèi)存受限的環(huán)境中有效地使用計(jì)數(shù)器,再到普通計(jì)數(shù)器的巧妙應(yīng)用。
    的頭像 發(fā)表于 12-15 09:24 ?639次閱讀

    詳解Mysql數(shù)據(jù)庫(kù)InnoDB存儲(chǔ)引擎事務(wù)

    關(guān)于Mysql數(shù)據(jù)庫(kù)InnoDB存儲(chǔ)引擎事務(wù)的一點(diǎn)理解
    發(fā)表于 05-13 10:11

    由于InnoDB MVCC導(dǎo)致的并發(fā)BUG介紹

    [原]記錄一個(gè)由于InnoDB MVCC導(dǎo)致的并發(fā)BUG
    發(fā)表于 07-17 09:46

    InnoDB鎖的特點(diǎn)和狀態(tài)查詢

    MySQL探秘(五)InnoDB鎖的類型和狀態(tài)查詢
    發(fā)表于 08-07 11:45

    分布式MySQL的InnoDB cluster

    分布式MySQL——InnoDB cluster和性能測(cè)試
    發(fā)表于 04-15 08:43

    基于修正LRU的壓縮Cache替換策略

    以優(yōu)化壓縮cache的替換策略為目標(biāo),提出一種優(yōu)化的基于修正LRU的壓縮cache替換策略MLRU-C。MLRU-C策略能利用壓縮cache中額外的tag資源,形成影子tag機(jī)制來(lái)探測(cè)并修正LRU替換策略的錯(cuò)誤
    發(fā)表于 04-15 09:51 ?36次下載

    關(guān)于InnoDB的內(nèi)存結(jié)構(gòu)及原理詳解

    除此之外還聊了一下MySQL和InnoDB的日志,和兩次寫,總的來(lái)說(shuō)算是一個(gè)入門級(jí)別的介紹,這篇文章就來(lái)詳細(xì)介紹一下InnoDB的內(nèi)存結(jié)構(gòu)。
    的頭像 發(fā)表于 04-16 16:15 ?2829次閱讀
    關(guān)于<b class='flag-5'>InnoDB</b>的內(nèi)存結(jié)構(gòu)及原理詳解

    innodb究竟是如何存數(shù)據(jù)的

    前言如果你使用過(guò)mysql數(shù)據(jù)庫(kù),對(duì)它的存儲(chǔ)引擎:innodb,一定不會(huì)感到陌生。 眾所周知,mysql5以前,默認(rèn)的存儲(chǔ)引擎是:myslam。但mysql5之后,默認(rèn)的存儲(chǔ)引擎已經(jīng)變成
    的頭像 發(fā)表于 10-09 15:41 ?1382次閱讀
    <b class='flag-5'>innodb</b>究竟是如何存數(shù)據(jù)的

    MySQL5.6 InnoDB支持全文檢索

    早期的 MySQL 中,InnoDB 并不支持全文檢索技術(shù), MySQL 5.6 開(kāi)始,InnoDB 開(kāi)始支持全文檢索。
    的頭像 發(fā)表于 11-12 15:14 ?1464次閱讀

    剖析MySQL InnoDB存儲(chǔ)原理(下)

    一、InnoDB存儲(chǔ)引擎內(nèi)存管理 1.1 概念: Buffer Pool:預(yù)分配的內(nèi)存池; Page:Buffer Pool的最小單位; Free list:空閑Page組成的鏈表;
    的頭像 發(fā)表于 02-15 15:47 ?464次閱讀
    剖析MySQL <b class='flag-5'>InnoDB</b>存儲(chǔ)原理(下)

    什么是list?

    list 容器,又稱雙向鏈表容器,即該容器的底層是以雙向鏈表的形式實(shí)現(xiàn)的。這意味著,list 容器中的元素可以分散存儲(chǔ)在內(nèi)存空間里,而不是必須存儲(chǔ)一整塊連續(xù)的內(nèi)存空間中。
    的頭像 發(fā)表于 02-27 15:52 ?2626次閱讀

    設(shè)計(jì)并實(shí)現(xiàn)一個(gè)滿足LRU約束的數(shù)據(jù)結(jié)構(gòu)

    LRUCache(int capacity)` 以 **「正整數(shù)」** 作為容量 `capacity` 初始化 `LRU` 緩存
    的頭像 發(fā)表于 06-07 17:05 ?1059次閱讀
    設(shè)計(jì)并實(shí)現(xiàn)一個(gè)滿足<b class='flag-5'>LRU</b>約束的數(shù)據(jù)結(jié)構(gòu)

    redis的lru原理

    Redis是一種基于內(nèi)存的鍵值數(shù)據(jù)庫(kù),它使用了LRU(Least Recently Used)算法來(lái)進(jìn)行緩存的數(shù)據(jù)淘汰。LRU算法的核心思想是最近最少使用的數(shù)據(jù)將會(huì)在未來(lái)也不常用,因此應(yīng)該優(yōu)先
    的頭像 發(fā)表于 12-05 09:56 ?671次閱讀

    關(guān)于LRU(Least Recently Used)的邏輯實(shí)現(xiàn)

    Used)算法是一種常用的緩存淘汰策略,其核心思想是:如果一個(gè)數(shù)據(jù)最近一段時(shí)間內(nèi)沒(méi)有被訪問(wèn)到,那么未來(lái)它被訪問(wèn)的可能性也很小。因此,當(dāng)緩存滿了的時(shí)候,最久未使用的數(shù)據(jù)會(huì)被淘汰。 LRU
    的頭像 發(fā)表于 11-12 11:47 ?403次閱讀
    關(guān)于<b class='flag-5'>LRU</b>(Least Recently Used)的邏輯實(shí)現(xiàn)
    大连娱网棋牌大厅| 云顶国际娱乐开户| 百家乐官网博彩资讯论坛| 金锁玉关24山砂水断| 百家乐概率计算过程| 博彩机| 百家乐官网软件代理| 济州岛百家乐的玩法技巧和规则 | E乐博娱乐城| 新天地百家乐官网的玩法技巧和规则| 百家乐园云鼎赌场娱乐网规则 | 云博备用网| 神话百家乐官网的玩法技巧和规则| 联众百家乐的玩法技巧和规则 | 顶尖百家乐官网的玩法技巧和规则 | 百家乐赌神| 百家乐| 网上百家乐投注法| 大发888ios版| 百家乐官网网上娱乐场开户注册 | 百家乐官网视频游戏会员| 百家乐园sun811| 交城县| 百家乐赢家电子书| 名门国际| 百家乐开户优惠多的平台是哪家 | 法老王娱乐城| 百家乐仿水晶筹码| 阿坝| 百家乐电话投注多少| 百家乐官网斗视频游戏| 百家乐凯时娱乐场| 百家乐官网视频小游戏| 百家乐vshow| 优博百家乐官网yobo88| 威尼斯人娱乐城真人游戏| 什么叫百家乐官网的玩法技巧和规则 | 百家乐庄闲和收益| 网络百家乐官网真假| 威尼斯人娱乐平台官网| 百家乐官网b28博你|