通過線上故障帶你看懂?MySQL?InnoDB?緩沖池
一、凌晨兩點,數(shù)據(jù)庫突然“卡死”了
某天凌晨,兩臺業(yè)務(wù)應(yīng)用同時報警。
接口 RT(響應(yīng)時間) 從原來的幾十毫秒飆升到 3 秒以上,應(yīng)用線程大量堆積,數(shù)據(jù)庫 CPU 卻并不算高,維持在 40% 左右。
第一反應(yīng)通常會懷疑:
- SQL 是否出現(xiàn)了慢查詢
- 是否存在鎖等待
- 是否有大事務(wù)
- 磁盤 IO 是否打滿
但實際排查后發(fā)現(xiàn):
- 慢 SQL 數(shù)量并不多
- 沒有明顯鎖沖突
- QPS 沒有明顯上漲
- CPU 也不高
真正異常的是:
SHOW ENGINE INNODB STATUS;
以及:
SHOW GLOBAL STATUS LIKE 'Innodb_buffer_pool%';
其中幾個指標非常異常:
- Buffer Pool 命中率明顯下降
- Free Buffers 接近 0
- Pages Read 持續(xù)暴漲
- 磁盤隨機讀 IO 飆高
此時基本可以確定:
問題出在 InnoDB Buffer Pool。
二、問題定位:Buffer Pool 正在“失效”
繼續(xù)分析監(jiān)控后,發(fā)現(xiàn)系統(tǒng)在故障前剛上線了一個數(shù)據(jù)統(tǒng)計任務(wù)。
這個任務(wù)有兩個特點:
- 會掃描大量歷史數(shù)據(jù)
- 查詢的數(shù)據(jù)幾乎不會重復(fù)訪問
也就是說:
大量冷數(shù)據(jù)正在不斷沖擊 Buffer Pool。
現(xiàn)象本質(zhì)
正常情況下:
熱點數(shù)據(jù)應(yīng)該長期駐留在內(nèi)存中。
但這個統(tǒng)計任務(wù)會不斷讀取新的數(shù)據(jù)頁,導(dǎo)致原本緩存中的熱點頁被大量淘汰。
結(jié)果就是:
業(yè)務(wù)原本可以直接命中的數(shù)據(jù),現(xiàn)在必須重新走磁盤讀取。
數(shù)據(jù)庫開始進入:
“緩存失效 → 磁盤 IO 暴漲 → 查詢變慢 → 連接堆積”
的惡性循環(huán)。
這也是很多 MySQL 線上抖動最典型的問題之一。
而理解這一切,必須先搞懂 InnoDB Buffer Pool 到底是什么。
三、什么是 InnoDB Buffer Pool
簡單來說:
Buffer Pool 是 InnoDB 的內(nèi)存緩存區(qū)。
它的核心作用是:
- 緩存數(shù)據(jù)頁
- 緩存索引頁
- 減少磁盤 IO
- 提高查詢性能
MySQL 的數(shù)據(jù)最終存儲在磁盤中。
但磁盤隨機讀取速度遠低于內(nèi)存。
因此 InnoDB 會把熱點數(shù)據(jù)提前加載到 Buffer Pool 中。
當 SQL 查詢數(shù)據(jù)時:
- 如果數(shù)據(jù)已經(jīng)在 Buffer Pool 中 → 直接讀取內(nèi)存
- 如果不在 → 從磁盤加載
這就是經(jīng)典的:
- Cache Hit(緩存命中)
- Cache Miss(緩存未命中)
通常線上高性能 MySQL:
Buffer Pool 命中率會維持在:
99% 以上
如果持續(xù)下降,數(shù)據(jù)庫性能通常會明顯惡化。
四、Buffer Pool 內(nèi)部是怎么工作的
1. 數(shù)據(jù)以 Page 為單位管理
InnoDB 并不是按“行”緩存數(shù)據(jù)。
而是按 Page(頁)管理。
默認每個 Page 大?。?/p>
16KB
讀取一行數(shù)據(jù)時:
整個 Page 都會被加載到 Buffer Pool。
因此:
即使只查詢一條記錄,也可能讀取 16KB 數(shù)據(jù)。
2. LRU 鏈表并不是真正的傳統(tǒng) LRU
很多文章會簡單說:
Buffer Pool 使用 LRU 淘汰數(shù)據(jù)。
但實際上,InnoDB 做了優(yōu)化。
它把 LRU 分成了兩部分:
- young 區(qū)
- old 區(qū)
默認比例大約:
5 : 3
新讀取的數(shù)據(jù)頁,先進入 old 區(qū)。
只有被再次訪問后,才會進入 young 區(qū)。
這樣設(shè)計是為了避免:
一次全表掃描,把真正熱點數(shù)據(jù)全部擠掉。
這也是 InnoDB 非常經(jīng)典的緩存保護機制。
3. Flush 機制
Buffer Pool 中的數(shù)據(jù)修改后:
不會立刻寫盤。
而是先修改內(nèi)存頁。
這種頁叫:
Dirty Page(臟頁)
后臺線程會異步刷盤。
這樣可以:
- 合并 IO
- 減少磁盤寫入
- 提升事務(wù)性能
但如果臟頁比例過高:
系統(tǒng)會觸發(fā)強制刷盤。
此時大量 IO 會導(dǎo)致數(shù)據(jù)庫明顯抖動。
五、為什么 Buffer Pool 問題會拖垮數(shù)據(jù)庫
生產(chǎn)環(huán)境中,最常見的問題主要有四類。
1. Buffer Pool 設(shè)置過小
這是最常見的問題。
如果內(nèi)存只有 2GB Buffer Pool:
但業(yè)務(wù)熱點數(shù)據(jù)有 20GB。
那么緩存必然頻繁淘汰。
數(shù)據(jù)庫會持續(xù)隨機讀磁盤。
性能下降非常明顯。
2. 大 SQL 掃描冷數(shù)據(jù)
例如:
SELECT * FROM order_history;
這種全表掃描會讀取大量冷頁。
導(dǎo)致熱點頁被擠出緩存。
線上業(yè)務(wù)隨后全部變慢。
很多“數(shù)據(jù)庫突然卡頓”,根因都在這里。
3. 臟頁比例過高
如果寫入壓力過大:
后臺刷盤跟不上。
臟頁會持續(xù)累積。
最終觸發(fā):
checkpoint flush
數(shù)據(jù)庫會瞬間產(chǎn)生大量 IO。
RT 抖動會非常明顯。
4. Buffer Pool 實例數(shù)不合理
高并發(fā)場景下:
多個線程會競爭 Buffer Pool 鎖。
因此 MySQL 引入:
innodb_buffer_pool_instances
把 Buffer Pool 切分為多個實例。
減少鎖競爭。
否則:
CPU 看起來不高,但線程等待會很多。
六、線上如何排查 Buffer Pool 問題
以下幾個指標非常關(guān)鍵。
1. 查看 Buffer Pool 命中率
SHOW GLOBAL STATUS LIKE 'Innodb_buffer_pool_read%';
重點關(guān)注:
- Innodb_buffer_pool_reads
- Innodb_buffer_pool_read_requests
命中率計算:
1 - (reads / read_requests)
如果低于:
99%
通常就需要關(guān)注。
2. 查看 Buffer Pool 使用情況
SHOW ENGINE INNODB STATUS;
重點觀察:
- Free buffers
- Database pages
- Modified db pages
如果 Free buffers 長期接近 0:
說明 Buffer Pool 壓力很大。
3. 觀察磁盤隨機讀
如果出現(xiàn):
- 磁盤 IO 飆升
- await 增大
- iops 激增
同時 Buffer Pool 命中率下降。
通常就是緩存失效。
七、生產(chǎn)環(huán)境優(yōu)化方案
1. 增大 Buffer Pool
這是最直接有效的方法。
通常建議:
物理內(nèi)存的 50% ~ 75%
專用數(shù)據(jù)庫服務(wù)器甚至可以更高。
例如:
innodb_buffer_pool_size=16G
這是性能提升最明顯的一項配置。
2. 避免大范圍全表掃描
歷史歸檔表:
- 盡量分頁
- 盡量走索引
- 避免 SELECT *
統(tǒng)計任務(wù)建議:
- 從庫執(zhí)行
- 低峰執(zhí)行
- 分批掃描
避免沖擊線上熱點緩存。
3. 調(diào)整 old 區(qū)策略
可以適當調(diào)整:
innodb_old_blocks_time
避免掃描頁快速進入 young 區(qū)。
對抗全表掃描污染效果明顯。
4. 控制臟頁比例
重點關(guān)注:
Innodb_buffer_pool_pages_dirty
必要時調(diào)整:
innodb_io_capacity innodb_io_capacity_max
讓后臺刷盤更平滑。
5. 合理配置 Buffer Pool Instances
大內(nèi)存機器建議:
innodb_buffer_pool_instances=8
避免熱點競爭。
但實例也不是越多越好。
過多會導(dǎo)致內(nèi)存碎片增加。
通常:
每個實例至少 1GB
比較合理。
八、總結(jié)
很多人優(yōu)化 MySQL 時:
只關(guān)注 SQL。
但實際上:
真正決定數(shù)據(jù)庫性能上限的,往往是內(nèi)存命中率。
Buffer Pool 本質(zhì)上就是:
MySQL 的“數(shù)據(jù)緩存核心”。
它決定了:
- 數(shù)據(jù)是否需要走磁盤
- IO 是否會暴漲
- 查詢是否穩(wěn)定
- 數(shù)據(jù)庫是否會突然抖動
線上大量“偶發(fā)性慢查詢”、
“數(shù)據(jù)庫突然變卡”、
“CPU 不高但 RT 很高”
背后都可能是 Buffer Pool 出了問題。
理解它的運行機制后,很多 MySQL 性能問題都會變得容易定位。
到此這篇關(guān)于次線上故障帶你看懂 MySQL InnoDB 緩沖池的文章就介紹到這了,更多相關(guān)MySQL InnoDB 緩沖池內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
mysql內(nèi)連接,連續(xù)兩次使用同一張表,自連接方式
這篇文章主要介紹了mysql內(nèi)連接,連續(xù)兩次使用同一張表,自連接方式,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2023-12-12
解決MySQL報錯incorrect?datetime?value?'0000-00-00?00:00
這篇文章主要給大家介紹了關(guān)于如何解決MySQL報錯incorrect?datetime?value?'0000-00-00?00:00:00'?for?column的相關(guān)資料,文中通過代碼示例介紹的非常詳細,需要的朋友可以參考下2023-08-08
對MySql經(jīng)常使用語句的全面總結(jié)(必看篇)
下面小編就為大家?guī)硪黄獙ySql經(jīng)常使用語句的全面總結(jié)(必看篇)。小編覺的挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2017-03-03
解決Navicat遠程連接MySQL出現(xiàn) 10060 unknow error的方法
這篇文章主要介紹了解決Navicat遠程連接MySQL出現(xiàn) 10060 unknow error的方法,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2019-12-12
MySQL百萬級數(shù)據(jù)量分頁查詢方法及其優(yōu)化建議
這篇文章主要介紹了MySQL百萬級數(shù)據(jù)量分頁查詢方法及其優(yōu)化建議,幫助大家更好的處理MySQL數(shù)據(jù)庫,感興趣的朋友可以了解下2020-08-08

