buffer pool是什么?
- 是一塊內存區域,當數據庫操作數據的時候,把硬盤上的數據加載到buffer pool,不直接和硬盤打交道,操作的是buffer pool里面的數據
- 數據庫的增刪改查都是在buffer pool上進行,和undo log/redo log/redo log buffer/binlog一起使用,后續會把數據刷到硬盤上
- 默認大小 128M
數據頁
- 磁盤文件被分成很多數據頁,一個數據頁里面有很多行數據
- 一個數據頁默認大小 16K
- 更新一行數據,實際上是把行數據所在的
數據頁
整個加載到buffer pool中
緩存頁
- buffer pool中存放的數據頁我們叫緩存頁,和磁盤上的數據頁是一一對應的,都是16KB
- 緩存頁的數據,是從磁盤上加載到buffer pool當中的
緩存頁描述信息(描述信息塊)
- 存的是 數據頁所屬的表空間號,數據頁編號,數據頁地址等信息
- 放在緩存頁的前面
- 每個描述信息塊大小是緩存頁的5%左右,大約是 1610240.05=800個字節
buffer pool初始化
1 數據庫只要一啟動,就會按照你設置的Buffer Pool大小,稍微再加大一點,去找操作系統申請一塊內存區域,作為Buffer Pool的內存區域
2 然后當內存區域申請完畢之后,數據庫就會按照默認的緩存頁的16KB的大小以及對應的800個字節左右的描述數據的大小,在Buffer Pool中划分出來一個一個的緩存頁和一個一個的他們對應的描述數據
free鏈
- 作用:幫助我們找到空閑的緩存頁
- 是一個雙向鏈表,鏈表節點是空閑的緩存頁對應的描述信息塊(空的緩存頁)
- 鏈表上除了描述信息塊,還有一個基礎節點,存儲了free鏈有多少個描述信息塊,也就是有多少個空閑的緩存頁
- 當我們加載數據的時候,會從free鏈中找到空閑的緩存頁,把數據頁的表空間號和數據頁號寫入描述信息塊;加載數據到緩存頁后,會把緩存頁對應的描述信息塊從free鏈表中移除
怎么知道數據頁是否被緩存?
- 數據庫中有一個 數據頁緩存哈希表,用表空間號+數據頁號,作為一個key,然后緩存頁的地址作為value
- 表空間號+數據頁號 = 緩存頁地址
什么是臟緩存頁?
- 被更新過的緩存頁,數據和磁盤上的數據不一致,所以是臟緩存頁
- 臟緩存頁的數據是要刷到磁盤上的
flush鏈表
- 是一個雙向鏈表,鏈表結點是被修改過的緩存頁的描述信息塊(更新過的緩存頁)
- 作用:幫我們找到臟緩存頁,也就是需要刷盤的緩存頁
- 和free鏈表一樣,也有一個基礎結點,鏈接首尾結點,並存儲了有多少個描述信息塊
- 最后要把flush鏈表上結點對應的緩存頁刷盤,后台線程會在MySQL不怎么繁忙的時候,找個時間把flush鏈表中的緩存頁都刷入磁盤中,這樣被你修改過的數據,遲早都會刷入磁盤的;緩存頁從flush鏈表中移除,加入到free鏈表當中
LRU鏈表
- 是一個雙向鏈表,鏈表結點是 非空的緩存頁對應的描述信息塊(有數據的緩存頁,包含更新過和未更新過的緩存頁,范圍比flush鏈表大,flush鏈表是它的子集)
- 作用:用來淘汰不常被訪問的緩存頁
- LRU鏈表分為熱數據區和冷數據區,冷數據區占了總鏈表的37%
- 冷數據區是不常訪問的緩存頁
- 熱數據區是經常訪問的緩存頁
- 加載數據的時候,緩存頁會放在冷數據區的頭部
- 數據頁加載到緩存頁后,在1s之后,訪問該緩存頁,該緩存頁會被移動到熱數據區頭部
- 數據頁剛加載到緩存頁后,在1s之內,訪問該緩存頁,該緩存頁是不會被移動到熱數據區頭部的
- 什么時候會lru中的緩存頁刷盤並清空?
- 當緩存頁用完的時候,把冷數據區尾部的緩存頁刷盤清空,緩存頁對應的信息描述塊從lru鏈表中移除,加入到free鏈表當中
- 有一個后台線程,他會運行一個定時任務,這個定時任務每隔一段時間就會把LRU鏈表的冷數據區域的尾部的一些緩存頁,刷入磁盤里去,清空這幾個緩存頁,把他們加入回free鏈表去;如果該緩存頁也在flush鏈表中(該緩存頁更新過),也需要把該緩存頁從flush鏈表中移除
- 當緩存頁用完的時候,把冷數據區尾部的緩存頁刷盤清空,緩存頁對應的信息描述塊從lru鏈表中移除,加入到free鏈表當中
- 熱數據區的前1/4的緩存頁如果被訪問,是不會移動到熱數據區頭部的;后3/4的緩存頁被訪問了,才會移動到熱數據區頭部
預讀機制
- 所謂預讀機制,說的就是當你從磁盤上加載一個數據頁的時候,他可能會連帶着把這個數據頁相鄰的其他數據頁,也加載到緩存里去
- 什么時候會觸發預讀機制?
- 有一個參數是innodb_read_ahead_threshold,他的默認值是56,意思就是如果順序的訪問了一個區里的多個數據頁,訪問的數據頁的數量超過了這個閾值,此時就會觸發預讀機制,把下一個相鄰區中的所有數據頁都加載到緩存里去
- 如果Buffer Pool里緩存了一個區里的13個連續的數據頁,而且這些數據頁都是比較頻繁會被訪問的,此時就會直接觸發預讀機制,把這個區里的其他的數據頁都加載到緩存里去
- 全表掃描的時候,select * from tableName 會把該表所有的數據頁都緩存到buffer pool當中
Buffer Pool的緩存頁以及幾個鏈表的使用回顧
- 數據庫啟動時,會申請內存創建buffer pool,buffer pool分成一個個緩存頁及其緩存頁描述信息塊,描述信息塊加入到free鏈表中
- 數據加載到一個緩存頁,free鏈表里會移除這個緩存頁,然后lru鏈表的冷數據區域的頭部會放入這個緩存頁
- 如果查詢了一個緩存頁,那么此時就會把這個緩存頁在lru鏈表中移動到熱數據區域去,或者在熱數據區域中也有可能會移動到頭部去
- 如果更新了緩存頁,會把該緩存頁加入到flush鏈表中
- 如果緩存頁不夠用了,會把lru冷數據區尾部的緩存頁刷盤,清空;該緩存頁從lru鏈表和flush鏈表中移除,加入到free鏈表中
- mysql后台線程也會定時把lru冷數據區尾部的緩存頁刷盤,清空;定時把flush鏈表中的緩存頁刷盤,清空,加入到free鏈表中
- 總結
- 一邊不停的加載數據到緩存頁里去,不停的查詢和修改緩存數據,然后free鏈表中的緩存頁不停的在減少,flush鏈表中的緩存頁不停的在增加,lru鏈表中的緩存頁不停的在增加和移動
- 另外一邊,你的后台線程不停的在把lru鏈表的冷數據區域的緩存頁以及flush鏈表的緩存頁,刷入磁盤中來清空緩存頁,然后flush鏈表和lru鏈表中的緩存頁在減少,free鏈表中的緩存頁在增加
文章和圖片參考救火隊長mysql