背景润脸,通常情況下柬脸,我們?yōu)榱藴p少數(shù)據(jù)庫(kù)壓力一班都會(huì)加個(gè)緩存機(jī)制,先從緩存查毙驯,查不到再?gòu)臄?shù)據(jù)庫(kù)查掺出,那么我們的數(shù)據(jù)庫(kù)也不想老讀磁盤渔伯,如何優(yōu)化呢流椒?那就是也加個(gè)緩存也就是這里的buffer pool了壤巷,
緩存頁(yè)數(shù)據(jù)與索引數(shù)據(jù)
,把磁盤上的數(shù)據(jù)加載到緩沖池铭段,避免每次訪問都進(jìn)行磁盤IO骤宣,起到加速訪問的作用。
一 .buffer pool是什么稠项?
咱們跟別的不一樣,咱們先說結(jié)論鲜结,再分析為啥展运。
- 緩沖池(buffer pool)是一種
降低磁盤訪問的機(jī)制
; - 緩沖池通常以·頁(yè)(page)·為單位緩存數(shù)據(jù)精刷;
- 緩沖池的常見管理算法是LRU拗胜,memcache,OS怒允,InnoDB都使用了這種算法埂软;
-
InnoDB對(duì)普通LRU進(jìn)行了優(yōu)化
:
- 將緩沖池分為老生代和新生代,入緩沖池的頁(yè)纫事,優(yōu)先進(jìn)入老生代勘畔,頁(yè)被訪問,才進(jìn)入新生代丽惶,以解決預(yù)讀失效的問題
- 頁(yè)被訪問炫七,且在老生代停留時(shí)間超過配置閾值的,才進(jìn)入新生代钾唬,以解決批量數(shù)據(jù)訪問万哪,大量熱數(shù)據(jù)被淘汰的問題
二 .buffer pool的設(shè)計(jì)思想
2.1 什么是預(yù)讀侠驯?
磁盤讀寫,并不是按需讀取奕巍,而是按頁(yè)讀取吟策,一次至少讀一頁(yè)數(shù)據(jù)(一般是4K),如果未來要讀取的數(shù)據(jù)就在頁(yè)中的止,就能夠省去后續(xù)的磁盤IO檩坚,提高效率。
為什么我們覺得需要預(yù)讀呢或者說預(yù)讀為什么有效冲杀?
因?yàn)閿?shù)據(jù)訪問效床,通常都遵循“集中讀寫”的原則,使用一些數(shù)據(jù)权谁,大概率會(huì)使用附近的數(shù)據(jù)剩檀,這就是所謂的“局部性原理”,它表明提前加載是有效的旺芽,確實(shí)能夠減少磁盤IO沪猴。
2.2 InnoDB的緩沖池設(shè)計(jì)的思路?
- 1.磁盤訪問按頁(yè)讀取能夠提高性能采章,所以緩沖池一般也是按頁(yè)緩存數(shù)據(jù)运嗜;
- 2.預(yù)讀機(jī)制啟示了我們,能把一些“可能要訪問”的頁(yè)提前加入緩沖池悯舟,避免未來的磁盤IO操作担租;
2.3 InnoDB是以什么算法,來管理這些緩沖頁(yè)呢抵怎?
通常情況下這類緩存一般使用的都是LRU算法奋救,比如我們操作系統(tǒng)、redis的數(shù)據(jù)淘汰反惕,但是我們Mysql在原LRU的基礎(chǔ)上做了一定的增強(qiáng)尝艘;
2.4 怎么知道數(shù)據(jù)頁(yè)是否被緩存?
數(shù)據(jù)庫(kù)中有一個(gè)數(shù)據(jù)頁(yè)緩存哈希表
我姿染,用表空間號(hào)+數(shù)據(jù)頁(yè)號(hào)
背亥,作為一個(gè)key,然后緩存頁(yè)的地址作為value
表空間號(hào)+數(shù)據(jù)頁(yè)號(hào) = 緩存頁(yè)地址
2.5 free鏈?zhǔn)歉缮兜模?/h4>
作用:幫助我們找到空閑的緩存頁(yè)
描述:是一個(gè)雙向鏈表悬赏,鏈表節(jié)點(diǎn)是空閑的緩存頁(yè)對(duì)應(yīng)的描述信息塊(空的緩存頁(yè))
記載進(jìn)free的時(shí)機(jī):
- 數(shù)據(jù)庫(kù)啟動(dòng)時(shí)狡汉,會(huì)申請(qǐng)內(nèi)存創(chuàng)建buffer pool,buffer pool分成一個(gè)個(gè)緩存頁(yè)及其緩存頁(yè)描述信息塊闽颇,描述信息塊加入到free鏈表中
- 如果緩存頁(yè)不夠用了轴猎,會(huì)把
lru冷數(shù)據(jù)區(qū)尾部的緩存頁(yè)
刷盤,清空进萄;該緩存頁(yè)從lru鏈表和flush鏈表中移除捻脖,加入到free鏈表中
- 如果緩存頁(yè)不夠用了轴猎,會(huì)把
- 3.mysql后臺(tái)線程也會(huì)定時(shí)把lru
冷數(shù)據(jù)區(qū)
尾部的緩存頁(yè)刷盤锐峭,清空;定時(shí)把flush鏈表中的緩存頁(yè)刷盤可婶,清空沿癞,加入到free鏈表中
- 鏈表上除了描述信息塊,還有一個(gè)基礎(chǔ)節(jié)點(diǎn)矛渴,存儲(chǔ)了free鏈有多少個(gè)描述信息塊椎扬,也就是有多少個(gè)空閑的緩存頁(yè)
-
當(dāng)我們加載數(shù)據(jù)的時(shí)候,會(huì)從free鏈中找到空閑的緩存頁(yè)具温,把數(shù)據(jù)頁(yè)的表空間號(hào)和數(shù)據(jù)頁(yè)號(hào)寫入描述信息塊蚕涤;加載數(shù)據(jù)到緩存頁(yè)后,會(huì)把緩存頁(yè)對(duì)應(yīng)的描述信息塊從free鏈表中移除image
2.6 什么是臟緩存頁(yè)铣猩?
被更新過的緩存頁(yè)揖铜,數(shù)據(jù)和磁盤上的數(shù)據(jù)不一致,所以是臟緩存頁(yè)
臟緩存頁(yè)的數(shù)據(jù)是要刷到磁盤上的
2.7 flush鏈表
是一個(gè)雙向鏈表达皿,鏈表結(jié)點(diǎn)是被修改過的緩存頁(yè)的描述信息塊
作用:幫我們找到臟緩存頁(yè)天吓,也就是需要刷盤的緩存頁(yè),如果更新了緩存頁(yè)峦椰,會(huì)把該緩存頁(yè)加入到flush鏈表中
和free鏈表一樣龄寞,也有一個(gè)基礎(chǔ)結(jié)點(diǎn),鏈接首尾結(jié)點(diǎn)汤功,并存儲(chǔ)了有多少個(gè)描述信息塊
最后要把flush鏈表上結(jié)點(diǎn)對(duì)應(yīng)的緩存頁(yè)刷盤物邑,后臺(tái)線程會(huì)在MySQL不怎么繁忙的時(shí)候,找個(gè)時(shí)間把flush鏈表中的緩存頁(yè)都刷入磁盤中滔金,這樣被你修改過的數(shù)據(jù)色解,遲早都會(huì)刷入磁盤的;緩存頁(yè)從flush鏈表中移除鹦蠕,加入到free鏈表當(dāng)中
三.buffer pool數(shù)據(jù)淘汰策略的設(shè)計(jì)思路
3.1 普通Lru有什么問題冒签?
- 1.容易出現(xiàn)
預(yù)讀失效
預(yù)讀:由于預(yù)讀(Read-Ahead)在抛,提前把頁(yè)放入了緩沖池钟病,但最終MySQL并沒有從頁(yè)中讀取數(shù)據(jù),稱為預(yù)讀失效刚梭。 - 2.容易出現(xiàn)
緩沖池污染
緩沖池污染:當(dāng)某一個(gè)SQL語(yǔ)句肠阱,要批量掃描大量數(shù)據(jù)時(shí),可能導(dǎo)致把緩沖池的所有頁(yè)都替換出去朴读,導(dǎo)致大量熱數(shù)據(jù)被換出屹徘,MySQL性能急劇下降,這種情況叫緩沖池污染衅金。
eg: select * from user where name like "%shenjian%";
雖然結(jié)果集可能只有少量數(shù)據(jù)噪伊,但這類like不能命中索引簿煌,必須全表掃描,就需要訪問大量的頁(yè):
buffer poll為了解決這兩個(gè)問題鉴吹,就在普通LRU基礎(chǔ)上做了一些優(yōu)化姨伟,盡量讓真正的熱點(diǎn)頁(yè)可以在buffer pool里存活時(shí)間更長(zhǎng)
3.2 buffer poll 優(yōu)化LRU算法,解決預(yù)讀失效做的優(yōu)化
他講Lru數(shù)據(jù)的頁(yè)鏈表豆励,分成兩個(gè)部分夺荒,新生代和老年代
具體方法是:
- 將LRU分為兩個(gè)部分:
新生代(new sublist)
老生代(old sublist)
- 將LRU分為兩個(gè)部分:
- 2.新老生代收尾相連,即:新生代的尾(tail)連接著老生代的頭(head)良蒸;
- 3.新頁(yè)(例如被預(yù)讀的頁(yè))加入緩沖池時(shí)技扼,只加入到老生代頭部:
如果數(shù)據(jù)真正被讀取(預(yù)讀成功)嫩痰,才會(huì)加入到新生代的頭部
如果數(shù)據(jù)沒有被讀取剿吻,則會(huì)比新生代里的“熱數(shù)據(jù)頁(yè)”更早被淘汰出緩沖池
3.3 buffer pool 增加“老年代停留時(shí)間窗口”機(jī)制解決緩沖池污染問題
MySQL緩沖池加入了一個(gè)“老生代停留時(shí)間窗口”的機(jī)制:
- 假設(shè)T=老生代停留時(shí)間窗口;
- 插入老生代頭部的頁(yè)始赎,即使立刻被訪問和橙,并不會(huì)立刻放入新生代頭部;短時(shí)間內(nèi)被大量加載的頁(yè)造垛,并不會(huì)立刻插入新生代頭部魔招,而是優(yōu)先淘汰那些短期內(nèi)僅僅訪問了一次的頁(yè)。
- 只有滿足“被訪問”并且“在老生代停留時(shí)間”大于T五辽,才會(huì)被放入新生代頭部办斑;
3.4 buffer pool 優(yōu)化的重要參數(shù)
3.4.1 innodb_buffer_pool_size
- 參數(shù):innodb_buffer_pool_size
- 介紹:配置緩沖池的大小,在內(nèi)存允許的情況下杆逗,DBA往往會(huì)建議調(diào)大這個(gè)參數(shù)乡翅,越多數(shù)據(jù)和索引放到內(nèi)存里,數(shù)據(jù)庫(kù)的性能會(huì)越好罪郊。
3.4.2 innodb_old_blocks_pct
- 參數(shù):innodb_old_blocks_pct
- 介紹:老生代占整個(gè)LRU鏈長(zhǎng)度的比例蠕蚜,默認(rèn)是37,即整個(gè)LRU中新生代與老生代長(zhǎng)度比例是63:37悔橄。
畫外音:如果把這個(gè)參數(shù)設(shè)為100靶累,就退化為普通LRU了。
3.4.3 innodb_old_blocks_time
- 參數(shù):innodb_old_blocks_time
- 介紹:老生代停留時(shí)間窗口癣疟,單位是毫秒挣柬,默認(rèn)是1000,即同時(shí)滿足“被訪問”與“在老生代停留時(shí)間超過1秒”兩個(gè)條件睛挚,才會(huì)被插入到新生代頭部邪蛔。
參考: