簡(jiǎn)介
InnoDB存儲(chǔ)引擎是基于磁盤存儲(chǔ)的尺锚,由于CPU速度與磁盤速度之間天差地別席楚,因此引入緩沖技術(shù)來提高整體性能趣惠。
緩沖池腮猖,是主存中InnoDB緩存被訪問的表和索引數(shù)據(jù)的區(qū)域鉴扫。緩沖池允許直接從內(nèi)存中處理經(jīng)常使用的數(shù)據(jù),從而加快處理速度缚够。
例如幔妨,客戶端進(jìn)行讀取、修改操作:
- 讀取操作:首先從磁盤上讀取到的頁(yè)放在緩沖池中谍椅,下一次讀取相同的頁(yè)時(shí)误堡,先判斷該頁(yè)是否在緩沖池中,若在雏吭,則稱作頁(yè)在緩沖池被命中锁施,否則需要去磁盤上讀取杖们;
- 修改操作:首先修改在緩沖池中的頁(yè)(修改過的頁(yè)悉抵,稱為臟頁(yè)),然后通過Checkpoint機(jī)制摘完,按照一定的頻率刷新到磁盤上姥饰。
在專用服務(wù)器上,高達(dá)80%的物理內(nèi)存通常分配給緩沖池孝治。
可以通過innodb_buffer_pool_size
配置緩沖池的大辛蟹唷(默認(rèn)8M),還可以通過innodb_buffer_pool_instances
配置緩沖池的數(shù)量(默認(rèn)1個(gè))谈飒。
主要注意的是岂座,緩沖池大小
innodb_buffer_pool_size
必須始終等于innodb_buffer_pool_chunk_size * innodb_buffer_pool_instances * K
(K是個(gè)大于0的常數(shù)),因此如果設(shè)置了個(gè)不滿足上述條件的innodb_buffer_pool_size
杭措,innoDB會(huì)幫你自動(dòng)調(diào)整為最接近上述條件的一個(gè)值费什。
緩沖池緩存數(shù)據(jù)對(duì)象
為了提高大容量讀操作的效率,緩沖池被劃分為可以容納多行的頁(yè)手素。
如圖所示鸳址,innoDB緩沖池緩存的數(shù)據(jù)對(duì)象主要有:
- 數(shù)據(jù)頁(yè);
- 索引頁(yè)泉懦;
- undo頁(yè)稿黍;
- 變更緩沖;
- 自適應(yīng)哈希索引祠斧;
- 數(shù)據(jù)字典闻察;
- 鎖信息拱礁;
緩沖池管理
為了提高緩存管理的效率琢锋,緩沖池被實(shí)現(xiàn)為頁(yè)鏈表辕漂,使用LRU算法來進(jìn)行管理,與傳統(tǒng)LRU算法不同的是吴超,InnoDB存儲(chǔ)引擎對(duì)LRU做了一些優(yōu)化钉嘹。當(dāng)需要向緩沖池中添加新頁(yè)時(shí),將淘汰最近最少使用的頁(yè)鲸阻,并將一個(gè)新頁(yè)添加到列表的midpoint位置跋涣,而不是放在首部。這種中點(diǎn)插入策略
將LRU劃分為兩個(gè)子列表:
- New Sublist:在midpoint前面鸟悴,new子列表陈辱,即熱點(diǎn)頁(yè);
-
Old Sublist:在midpoint后面细诸,old子列表沛贪。
image
如圖所示,new子列表占用5/8的空間震贵,midpoint正處于列表3/8的位置利赋,old子列表占用3/8的空間⌒上担可以通過innodb_old_blocks_pct
(默認(rèn)37媚送,按百分比算=3/8)配置midpoint的位置。
算法邏輯:假設(shè)用戶讀取某個(gè)緩沖池中沒有的頁(yè)寇甸。
- 先去磁盤讀取頁(yè)到緩沖池塘偎;
- 該頁(yè)對(duì)LRU來說是一個(gè)新的頁(yè),因此會(huì)把它加到midpoint幽纷,也就是old子列表的首部式塌;
- 如果該頁(yè)在之后的某段時(shí)間被訪問了,LRU算法會(huì)認(rèn)為它是熱點(diǎn)頁(yè)友浸,將它加到new子列表的首部峰尝,這個(gè)操作被稱為
page made young
;
InnoDB還提供一個(gè)配置參數(shù)innodb_old_blocks_time
延遲新插入的頁(yè)收恢,被加到new子列表的首部的時(shí)間武学。比如說,假設(shè)設(shè)置了innodb_old_blocks_time=1000
伦意,這個(gè)時(shí)候有個(gè)新頁(yè)被加到midpoint位置火窒,在1000ms期間,無論該頁(yè)被訪問多少次驮肉,都沒辦法被加到new子列表的首部熏矿。這個(gè)操作被稱為page not made young
。
為什么InnoDB不使用傳統(tǒng)的LRU算法?答案很明顯票编,假設(shè)某張學(xué)生表有1000個(gè)學(xué)生褪储,現(xiàn)在通過
select * from student where name = '張三'
,這條sql做了學(xué)生表的全表掃描慧域,innoDB會(huì)將掃到的頁(yè)都存放到緩沖池中鲤竹,也就是加到LRU。如果是傳統(tǒng)LRU昔榴,則掃描到一個(gè)頁(yè)辛藻,就加到首部。因此把大量的頁(yè)(可能這些頁(yè)只用一次互订,后面就不再使用了)放到了首部吱肌,導(dǎo)致原先熱點(diǎn)數(shù)據(jù)被擠出來。
查看緩沖池的運(yùn)行狀態(tài)
可以通過SHOW ENGINE INNODB STATUS;
查看仰禽。
參數(shù)名稱 | 描述 |
---|---|
Total memory allocated | 分配給緩沖池的總內(nèi)存岩榆,以字節(jié)為單位 |
Dictionary memory allocated | 分配給InnoDB數(shù)據(jù)字典的總內(nèi)存,以字節(jié)為單位 |
Buffer pool size | 分配給緩沖池的頁(yè)的總大小 |
Free buffers | 緩沖池空閑列表中頁(yè)的總大小 |
Database pages | 緩沖池LRU列表中的頁(yè)的總大小 |
Old database pages | 緩沖池LRU舊子列表中的頁(yè)的總大小 |
Modified db pages | 緩沖池中當(dāng)前修改的頁(yè)數(shù) |
Pending reads | 等待讀入緩沖池的緩沖池頁(yè)數(shù) |
Pending writes LRU | 緩沖池中從LRU列表底部等待寫入的舊臟頁(yè)數(shù)量 |
Pending writes flush list | 在檢查點(diǎn)期間要刷新的緩沖池臟頁(yè)的數(shù)目 |
Pending writes single page | 緩沖池中等待寫入的獨(dú)立頁(yè)數(shù)量 |
Pages made young | 在緩沖池LRU列表中變?yōu)槟贻p的頁(yè)面總數(shù) |
Pages made not young | 緩沖池LRU列表中未變?yōu)槟贻p的頁(yè)面總數(shù) |
youngs/s | 變年輕的頻率 |
non-youngs/s | 沒有變年輕的頻率 |
Pages read | 從緩沖池中讀取的頁(yè)面總數(shù) |
Pages created | 在緩沖池中創(chuàng)建的頁(yè)面總數(shù) |
Pages written | 從緩沖池寫入的頁(yè)面總數(shù) |
reads/s | 從緩沖池中讀取的頁(yè)面頻率 |
creates/s | 在緩沖池中創(chuàng)建的頁(yè)面頻率 |
writtes/s | 從緩沖池寫入的頁(yè)面頻率 |
Buffer pool hit rate | 緩沖池頁(yè)命中率 |
young-making rate | 頁(yè)面訪問導(dǎo)致頁(yè)面年輕的平均命中率 |
not (young-making rate) | 頁(yè)面訪問沒有導(dǎo)致頁(yè)面年輕的平均命中率 |
Pages read ahead | 預(yù)讀操作的每秒平均次數(shù) |
Pages evicted without access | 被淘汰坟瓢,而無法從緩沖池中訪問頁(yè)面的每秒平均數(shù)量 |
Random read ahead | 隨機(jī)預(yù)讀操作的每秒平均次數(shù) |
LRU len | 緩沖池LRU列表中的頁(yè)的總大小 |
unzip_LRU len | 緩沖池解壓縮LRU列表中頁(yè)的總大小 |
I/O sum | 最近50秒內(nèi)訪問緩沖池LRU列表頁(yè)的總數(shù) |
I/O cur | 訪問緩沖池LRU列表頁(yè)的總數(shù) |
I/O unzip sum | 最近50秒內(nèi)訪問緩沖池解壓縮LRU列表頁(yè)的總數(shù) |
I/O unzip cur | 訪問緩沖池解壓縮LRU列表頁(yè)的總數(shù) |
從上面可知勇边,‘Free buffers’代表的是緩沖池空閑列表中頁(yè)的總大小,當(dāng)數(shù)據(jù)庫(kù)啟動(dòng)時(shí)折联,LRU的列表是空的粒褒,即沒有任何頁(yè)。這時(shí)诚镰,頁(yè)都放到Free List中奕坟,當(dāng)需要從緩沖池中分配頁(yè)時(shí),首先從Free List中查找是否有空閑頁(yè)清笨,如果有則從Free List移到LRU 列表中月杉;否則,根據(jù)LRU算法抠艾,淘汰末尾的頁(yè)苛萎,騰出來給新的頁(yè)用。
其次检号,‘Pending writes flush list’表示臟頁(yè)列表的總頁(yè)數(shù)腌歉,在LRU列表中的頁(yè)被修改后,成為臟頁(yè)齐苛。Flush list是來管理臟頁(yè)列表翘盖,負(fù)責(zé)將臟頁(yè)刷回磁盤。當(dāng)然LRU列表也會(huì)存儲(chǔ)在臟頁(yè)凹蜂,保證緩沖池頁(yè)的可用性馍驯,兩者并不沖突阁危。
另外,有一個(gè)重要的參數(shù)‘Buffer pool hit rate’命中率等于100%汰瘫,說明緩沖池運(yùn)行狀態(tài)非常好欲芹,通常來講,該值不應(yīng)該小于95%吟吝,如果發(fā)生這種情況,考慮是否是因?yàn)槿頀呙枰鸬腖RU被污染的問題颈娜,可以適當(dāng)調(diào)整innodb_old_blocks_time
大小剑逃,或者避免全表掃描。
最后官辽,看下‘LRU len: 5720, unzip_LRU len: 0’蛹磺。‘LRU lenInnoDB’代表LRU列表頁(yè)的總數(shù)量(包含unzip_LRU)同仆,‘unzip_LRU’代表解壓縮(說明被壓縮過了萤捆,才需要解壓縮)LRU列表中頁(yè)的總大小。緩沖池頁(yè)的大小默認(rèn)是16K俗批,對(duì)于非16K(原本16K的頁(yè)壓縮為1K,2K,4K,8K)俗或,是通過unzip_LRU列表進(jìn)行管理。unzip_LRU管理方式如下:假設(shè)需要申請(qǐng)一個(gè)4K的頁(yè)岁忘。
- 檢查unzip_LRU列表是否有空閑的4K頁(yè),有則直接使用;如果沒有九巡,則往下走倦畅;
- 檢查是否有空閑的8K頁(yè),有則切割成兩個(gè)4K頁(yè)麻汰,一個(gè)直接使用速客,一個(gè)存放到unzip_LRU列表;如果沒有五鲫,則往下走溺职;
- 從LRU列表申請(qǐng)一個(gè)16K的空閑頁(yè),切割成一個(gè)8K位喂,兩個(gè)4K辅愿,分別存放到unzip_LRU列表中。