點一下關注吧!!!非常感謝!!持續更新!!!
🚀 AI篇持續更新中!(長期更新)
AI煉丹日志-30-新發布【1T 萬億】參數量大模型!Kimi?K2開源大模型解讀與實踐,持續打造實用AI工具指南!📐🤖
💻 Java篇正式開啟!(300篇)
目前2025年07月28日更新到:
Java-83 深入淺出 MySQL 連接、線程、查詢緩存與優化器詳解
MyBatis 已完結,Spring 已完結,Nginx已完結,Tomcat已完結,分布式服務正在更新!深入淺出助你打牢基礎!
📊 大數據板塊已完成多項干貨更新(300篇):
包括 Hadoop、Hive、Kafka、Flink、ClickHouse、Elasticsearch 等二十余項核心組件,覆蓋離線+實時數倉全棧!
大數據-278 Spark MLib - 基礎介紹 機器學習算法 梯度提升樹 GBDT案例 詳解
InnoDB存儲結構
在 MySQL 5.5 版本開始之后,默認使用 InnoDB 存儲引擎,它擅長處理事務,具有自動奔潰恢復的特性,在日常開發中使用非常廣泛。
下面是 InnoDB 引擎架構圖,主要分為內存結構和磁盤結構兩大部分。
內存結構
內存結構主要包括 Buffer Pool、Change Buffer、Adaptive Hash Index 和 Log Buffer 四大組件。
Buffer Pool(緩沖池)
基本概念
緩沖池(Buffer Pool,簡稱BP)是InnoDB存儲引擎的核心內存區域,主要用于緩存表數據和索引數據,以減少磁盤I/O操作,顯著提升數據庫性能。
存儲單元
- Page(頁):BP的基本管理單位
- 默認大小:16KB(可通過
innodb_page_size
參數配置) - 包含類型:數據頁、索引頁、undo頁、插入緩沖頁等
- 每個Page都有對應的控制信息(約5%額外空間)
- 默認大小:16KB(可通過
數據結構
采用改進的鏈表結構管理:
- Free List:維護所有空閑頁
- LRU List:管理已使用的頁,采用改進的LRU算法
- 分為young sublist(5/8)和old sublist(3/8)
- 新頁插入到old sublist中部
- 頁被訪問時可能移至young區
- Flush List:記錄被修改過的臟頁(按LSN排序)
工作流程示例
-
當查詢需要讀取某頁數據時:
- 先在BP中查找(哈希表快速定位)
- 命中則直接使用內存數據(邏輯讀)
- 未命中則從磁盤加載(物理讀)
-
寫操作流程:
關鍵配置參數
innodb_buffer_pool_size
:總大小(建議設為物理內存的50-70%)innodb_buffer_pool_instances
:實例數(減少鎖爭用)innodb_old_blocks_time
:頁轉入young區前的保護期
性能影響
- 命中率計算公式:
Hit Ratio = 1 - (innodb_buffer_pool_reads / innodb_buffer_pool_read_requests)
- 良好實踐:監控命中率(建議保持在95%以上),通過預熱腳本提升初始性能
應用場景
- 熱點數據訪問:頻繁訪問的數據會長期駐留BP
- 事務處理:修改數據先在BP中完成,再異步刷盤
- 全表掃描:通過
innodb_old_blocks_time
避免擠出熱點數據
Page 管理機制詳細說明
● free page(空閑頁):
- 完全未被使用的頁,處于待分配狀態
- 不包含任何有效數據
- 當需要新頁面時,首先從這里分配
- 示例:當有新數據插入且緩沖區中沒有可用空間時,會從free list獲取free page
● clean page(干凈頁):
- 已被使用的頁,但數據與磁盤一致
- 數據未被修改過
- 可以直接替換而不需要寫回磁盤
- 示例:執行SELECT操作讀取的數據頁,在未被修改前都是clean page
● dirty page(臟頁):
- 已被使用且被修改過的頁
- 內存中的數據與磁盤不一致
- 需要被刷寫到磁盤才能釋放
- 示例:執行UPDATE操作修改數據后,對應的數據頁就變成dirty page
鏈表管理機制詳細說明
● free list(空閑鏈表):
- 維護所有可分配的空閑頁
- 采用簡單的鏈表結構管理
- 當需要新頁時,從這里快速獲取
- 應用場景:新查詢需要加載數據頁時
● flush list(刷新鏈表):
- 維護所有需要刷盤的臟頁
- 按照頁面的第一次修改時間排序(oldest_modification)
- 采用WAL機制,確保事務持久性
- 與LRU list獨立運作
- 刷盤策略:后臺線程定期檢查,在系統負載低時批量寫入
● lru list(最近最少使用鏈表):
- 采用改進的LRU算法管理
- 結構劃分:
- new sublist(新子列表,占5/8):
- 存放熱點數據
- 新訪問的頁先加入這里
- old sublist(老子列表,占3/8):
- 存放較冷數據
- 新頁默認先加入這里
- new sublist(新子列表,占5/8):
- 頁面移動規則:
- 新訪問的頁先插入到old sublist頭部
- 再次被訪問時,移動到new sublist頭部
- 長時間未被訪問的頁會逐漸向尾部移動
- 當需要空間時,優先淘汰old sublist尾部的頁
- 冷數據保護機制:
- 設置innodb_old_blocks_time參數
- 防止全表掃描污染緩沖區
改進型LRU算法(LRU with midpoint insertion)
● 普通LRU(Least Recently Used)算法:
- 采用簡單的末尾淘汰法,使用雙向鏈表結構管理緩存頁
- 新數據總是從鏈表頭部插入,成為最新的數據
- 當需要釋放空間時,從鏈表末尾淘汰最久未被訪問的數據
- 示例:一個包含A->B->C的LRU鏈表,訪問B后會變成B->A->C
● 改進型LRU算法(MySQL InnoDB實現):
- 將LRU鏈表劃分為兩個區域:
- new子鏈表(占5/8):存儲熱點數據
- old子鏈表(占3/8):存儲潛在淘汰數據
- 關鍵改進點:
- 新元素插入位置:不是直接插入鏈表頭部,而是從中間位置(midpoint,即new和old的交界處)插入
- 動態調整機制:
- 如果數據很快被再次訪問(首次訪問后的1秒內),該page會向new子鏈表頭部移動
- 未被訪問的數據會逐步向old子鏈表尾部移動
- 淘汰策略:優先從old子鏈表尾部淘汰頁面
具體工作流程:
-
數據讀取階段:
- 當需要將新Page數據加載到Buffer Pool時
- InnoDB引擎首先檢查free list是否有可用空閑頁
-
內存分配判斷:
- 情況1:存在足夠空閑頁
- 從free list移除對應的free page
- 將該page放入LRU鏈表的midpoint位置(即old子鏈表頭部)
- 設置首次訪問時間戳
- 情況2:無足夠空閑頁
- 觸發LRU淘汰機制:
- 從LRU鏈表old子表尾部選擇待淘汰頁
- 如果該頁是臟頁,先寫入磁盤
- 釋放該頁內存空間
- 將新page插入到midpoint位置
- 觸發LRU淘汰機制:
- 情況1:存在足夠空閑頁
-
訪問優化:
- 對于已在LRU鏈表中的page:
- 若在首次訪問后1秒內再次被訪問:
- 將其移至new子鏈表頭部
- 否則保持原位或向old子鏈表尾部移動
- 若在首次訪問后1秒內再次被訪問:
- 對于已在LRU鏈表中的page:
應用場景優勢:
- 有效防止全表掃描等批量操作污染熱點數據
- 更平滑的熱點數據遷移過程
- 示例:一個包含1000個page的Buffer Pool,625個page屬于new區域,375個page屬于old區域,新插入的page會先放在第626的位置
Buffer Pool 配置參數:
● show variables like ‘%innodb_page_size%’ 查看page頁大小
● show variables like ‘%innodb_old%’ 查看 LRU list 中 old列表參數
● show variables like ‘%innodb_buffer%’ 查看 Buffer Pool 參數
Change Buffer
寫緩沖區(Change Buffer,簡稱CB)是InnoDB存儲引擎中一種優化非唯一普通索引更新的重要機制。在進行DML(INSERT/UPDATE/DELETE)操作時,如果目標數據頁不在緩沖池(Buffer Pool)中,InnoDB不會立即從磁盤加載該頁,而是先將這些變更記錄在Change Buffer中。待未來該數據頁被讀取時,再將Change Buffer中的變更合并到緩沖池中。
核心特性:
-
空間占用:
- Change Buffer占用Buffer Pool的空間,默認配置為25%(通過參數
innodb_change_buffer_max_size
控制) - 最大可配置為50%,建議根據業務讀寫比例調整:
- 寫密集型業務可適當調高
- 讀密集型業務建議降低配置
- Change Buffer占用Buffer Pool的空間,默認配置為25%(通過參數
-
工作流程對比:
-
記錄存在Buffer Pool:
- 直接修改緩沖池中的頁
- 僅需一次內存操作
-- 示例:當執行UPDATE時,若數據頁已在內存 UPDATE users SET name='張三' WHERE id=1; -- 內存直接修改
-
記錄不存在Buffer Pool:
- 將變更寫入Change Buffer
- 避免立即的磁盤IO
- 后續讀取時合并變更
-- 示例:首次更新非活躍數據 UPDATE order_history SET status=2 WHERE order_id=10086; -- 寫入ChangeBuffer
-
-
合并觸發時機:
- 主動讀取:當執行SELECT查詢需要加載該數據頁時
- 后臺線程:由master線程定期合并
- 空間不足:當Change Buffer空間達到閾值時
適用限制:
僅適用于非唯一普通索引頁,主要原因如下:
-
唯一性約束驗證:
- 唯一索引(包括主鍵)必須保證數據唯一性
- 每次修改必須檢查磁盤現有數據
- 例如:
ALTER TABLE products ADD UNIQUE INDEX idx_sku(sku); -- 以下操作必須立即校驗唯一性 INSERT INTO products(sku) VALUES('A1001');
-
強制磁盤加載:
- 校驗過程會觸發磁盤讀取
- 數據頁會被加載到Buffer Pool
- 后續修改直接在緩沖池完成
-
典型應用場景:
- 日志表的時間戳索引
- 訂單歷史表的非關鍵字段索引
- 批量導入時的輔助索引更新
監控建議:
通過以下命令查看Change Buffer狀態:
SHOW ENGINE INNODB STATUS\G
-- 重點關注:
-- INSERT BUFFER AND ADAPTIVE HASH INDEX
-- merged operations: insert/delete mark/purge
注:在SSD存儲環境下,因隨機IO性能提升,Change Buffer的收益會相對降低,此時可適當減小其配置比例。
Adaptive Hash Index
自適應哈希索引,用于優化對 BP 數據的查詢,InnoDB 存儲引擎會在監控對表索引的查找,如果觀察到建立哈希索引可以帶來速度的提升,則建立哈希索引,所以稱之為自適應。
InnoDB 存儲引擎會自動根據訪問的頻率和模式來為某些頁面建立哈希索引。
Log Buffer(日志緩沖區)
日志緩沖區是數據庫系統中一個重要的內存區域,主要用于臨時存儲即將寫入磁盤日志文件(包括Redo日志和Undo日志)的數據。這個緩沖區作為磁盤I/O操作的緩沖層,可以顯著提高數據庫的寫入性能。
主要功能和工作原理
-
數據緩沖:所有DML操作(如INSERT、UPDATE、DELETE)產生的Redo和Undo日志都會先寫入Log Buffer,而不是直接寫入磁盤。Redo日志記錄數據頁的物理變化,用于崩潰恢復;Undo日志記錄事務前的數據狀態,用于事務回滾。
-
自動刷新機制:
- 當緩沖區空間寫滿時(達到innodb_log_buffer_size設置的大小),系統會自動將緩沖區內容刷新到磁盤的日志文件中。
- 對于大事務(如涉及BLOB或多行更新的操作),增大日志緩沖區可以減少磁盤I/O次數,提高性能。
-
手動刷新控制:通過innodb_flush_log_at_trx_commit參數可以配置不同的日志刷新策略:
- 0:每隔1秒執行一次日志寫入和刷盤操作(將LogBuffer內容寫入OS Cache,然后從OS Cache刷到磁盤文件)。這種模式性能最好,但可能在系統崩潰時丟失最多1秒的數據。
- 1(默認值):每次事務提交時立即執行日志寫入和刷盤操作。這種模式最安全,不會丟失數據,但會產生頻繁的I/O操作,影響性能。
- 2:每次事務提交時立即將日志寫入OS Cache,但每隔1秒才執行一次刷盤操作。這種模式在系統崩潰時可能會丟失1秒數據,但相比模式1有更好的性能。
實際應用場景
- 高并發事務系統:對于需要處理大量短事務的系統,適當增大innodb_log_buffer_size(如設置為8MB或16MB)可以減少磁盤I/O壓力。
- 批量數據處理:在執行大批量數據導入或更新時,可以臨時將innodb_flush_log_at_trx_commit設為0或2,完成后恢復為1。
- 關鍵業務系統:對數據安全性要求高的系統應保持默認設置(innodb_flush_log_at_trx_commit=1),確保每次事務提交后數據立即持久化。
性能優化建議
- 監控日志緩沖區使用情況,如果經常出現等待日志緩沖區空間的情況,應考慮增大緩沖區大小。
- 在SSD存儲環境中,可以適當降低日志刷新頻率,因為SSD的隨機寫入性能較好。
- 對于主從復制環境,從庫可以配置為較寬松的日志刷新策略以提高復制性能。