InnoDB 內存結構和磁盤結構

一條更新 SQL 是如何執行的?
update 操作其實包括了更新、插入和刪除。如果有看過 MyBatis 的源碼,應該知道 Executor 裏面也只有 doQuery()和 doUpdate()的方法,

更新流程和查詢流程有什麼不同呢?
基本流程也是一致的,也就是說,它也要經過解析器、優化器的處理,最後交給執行器。區別就在於拿到符合條件的數據之後的操作。

緩衝池 Buffer Pool
首先,InnnoDB 的數據都是放在磁盤上的,InnoDB 操作數據有一個最小的邏輯單位,叫做頁(索引頁和數據頁)。我們對於數據的操作,不是每次都直接操作磁盤,因爲磁盤的速度太慢了。InnoDB 使用了一種緩衝池的技術,也就是把磁盤讀到的頁放到一塊內存區域裏面。這個內存區域就叫 Buffer Pool

修改數據的時候,先修改緩衝池裏面的頁。內存的數據頁和磁盤數據不一致的時候,我們把它叫做髒頁。InnoDB 裏面有專門的後臺線程把 Buffer Pool 的數據寫入到磁盤,每隔一段時間就一次性地把多個修改寫入磁盤,這個動作就叫做刷髒

Buffer Pool 是 InnoDB 裏面非常重要的一個結構,它的內部又分成幾塊區域

我們來認識一下 InnoDB 的內存結構和磁盤結構:

在這裏插入圖片描述
內存結構:
Buffer Pool 主要分爲 3 個部分: Buffer Pool、Change Buffer、Adaptive HashIndex,另外還有一個(redo)log buffer

Buffer Pool
Buffer Pool 緩存的是頁面信息,包括數據頁、索引頁。查看服務器狀態,裏面有很多跟 Buffer Pool 相關的信息:
SHOW STATUS LIKE ‘%innodb_buffer_pool%’;
Buffer Pool 默認大小是 128M(134217728 字節),可以調整

內存的緩衝池寫滿了怎麼辦?(Redis 設置的內存滿了怎麼辦?)
InnoDB 用 LRU算法來管理緩衝池(鏈表實現,不是傳統的 LRU,分成了 young 和 old),經過淘汰留下的數據就是熱點數據

Change Buffer
在更新數據的時候,如果這個數據頁不是唯一索引(索引的值不允許重複),也就不需要從磁盤加載索引頁判斷數據是不是重複(唯一性檢查)。這種情況下可以先把修改記錄在內存的緩衝池中,從而提升更新語句(Insert、Delete、Update)的執行速度。
這一塊區域就是 Change Buffer。5.5 之前叫 Insert Buffer 插入緩衝,現在也能支持 delete 和 update,最後把 Change Buffer 記錄到數據頁的操作叫做 merge

什麼時候發生 merge?
有幾種情況:在訪問這個數據頁的時候,或者通過後臺線程、或者數據庫 shut down、redo log 寫滿時觸發
如果數據庫大部分索引都是非唯一索引,並且業務是寫多讀少,不會在寫數據後立刻讀取,就可以使用 Change Buffer(寫緩衝)。寫多讀少的業務,調大這個值:

SHOW VARIABLES LIKE 'innodb_change_buffer_max_size';

Adaptive Hash Index
索引應該是放在磁盤的,爲什麼要專門把一種哈希的索引放到內存?

Log Buffer(redo)和redo log
如果 Buffer Pool 裏面的髒頁還沒有刷入磁盤時,數據庫宕機或者重啓,這些數據丟失。如果寫操作寫到一半,甚至可能會破壞數據文件導致數據庫不可用,怎麼辦?

爲了避免這個問題,InnoDB 把所有的修改操作專門寫入一個日誌文件,並且在數據庫啓動時從這個文件進行恢復操作(實現 crash-safe)——用它來實現事務的持久性。

這個文件就是磁盤的 redo log(叫做重做日誌),對應於/var/lib/mysql/目錄下的ib_logfile0 和 ib_logfile1,每個 48M
這 種 日 志 和 磁 盤 配 合 的 整 個 過 程 , 其 實 就 是 MySQL 裏 的 WAL 技 術(Write-Ahead Logging),它的關鍵點就是先寫日誌,再寫磁盤。

當然redo log主要是用於異常回復,數據來源還是從 buffer pool 裏面通過刷盤策略刷新到DB File裏面

  • redolog大小是固定的,兩個48M的 文件,滿了之後會覆蓋老的數據
  • undo log :事務撤銷回滾日誌,記錄事務發生之前的狀態
  • undo log 是邏輯日誌,redo log是物理日誌(兩個日誌都屬於存儲引擎層)

一個修改SQL的執行過程,例如:set name = ‘penyuyan’ where id = 1;
1、事務開始,從內存或磁盤取到這條數據(修改數據前要先取得數據),返回給 Server 的執行器
2、執行器修改這一行數據的值爲 penyuyan;
3、記錄 name=qingshan 到 undo log;
4、記錄 name=penyuyan 到 redo log;
5、調用存儲引擎接口,在內存(Buffer Pool)中修改 name=penyuyan;
6、事務提交

5.bin log 服務層的日誌文件,可以被所有的存儲引擎共用

binlog 以事件的形式記錄了所有的 DDL 和 DML 語句(因爲它記錄的是操作而不是
數據值,屬於邏輯日誌),可以用來做主從複製和數據恢復

在開啓了 binlog 功能的情況下,我們可以把 binlog 導出成 SQL 語句,把所有的操作重放一遍,來實現數據的恢復。
binlog 的另一個功能就是用來實現主從複製,它的原理就是從服務器讀取主服務器的 binlog,然後執行一遍

磁盤結構

表空間可以看做是 InnoDB 存儲引擎邏輯結構的最高層,所有的數據都存放在表空間中。InnoDB 的表空間分爲 5 大類:

(1)系統表空間 system tablespace
默認情況下 InnoDB 存儲引擎有一個共享表空間(對應文件/var/lib/mysql/ibdata1),也叫系統表空間
InnoDB 系統表空間包含 InnoDB 數據字典和雙寫緩衝區,Change Buffer 和 UndoLogs),如果沒有指定 file-per-table,也包含用戶創建的表和索引數據:
1、undo 有獨立的表空間。
2、數據字典:由內部系統表組成,存儲表和索引的元數據(定義信息)。
3、雙寫緩衝(InnoDB 的一大特性):
因爲InnoDB 的頁和操作系統的頁大小不一致,InnoDB 頁大小爲 16K,操作系統頁
大小爲 4K,InnoDB 的頁寫入到磁盤時,一個頁需要分 4 次寫。
如果存儲引擎正在寫入頁的數據到磁盤時發生了宕機,可能出現頁只寫了一部分的
情況,比如只寫了 4K,就宕機了,這種情況叫做部分寫失效(partial page write),可
能會導致數據丟失。

show variables like 'innodb_doublewrite';

我們不是有 redo log 嗎?但是有個問題,如果這個頁本身已經損壞了,怎麼辦?所以在對於應用 redo log 之前,需要一個頁的副本。如果出現了寫入失效,就用頁的副本來還原這個頁,然後再應用 redo log。這個頁的副本就是 double write,InnoDB 的雙寫技術。通過它實現了數據頁的可靠性。
因爲 double write 是順序寫入的,不會帶來很大的開銷。
在默認情況下,所有的表共享一個系統表空間,這個文件會越來越大,而且它的空間不會收縮

2)獨佔表空間 file-per-table tablespaces
我們可以讓每張表獨佔一個表空間。這個開關通過 innodb_file_per_table 設置,默認開啓。
SHOW VARIABLES LIKE ‘innodb_file_per_table’
開啓後每張表會開闢一個表空間,存放在數據目錄下的 ibd 文件(例如
/var/lib/mysql/gupao/user_innodb.ibd),存放表的索引和數據
但是其他類的數據,如回滾(undo)信息,插入緩衝索引頁、系統事務信息,二次寫緩衝(Double write buffer)等還是存放在原來的共享表空間內

(3)通用表空間 general tablespaces
通用表空間也是一種共享的表空間,跟 ibdata1 類似。可以創建一個通用的表空間,用來存儲不同數據庫的表,數據路徑和文件可以自定義

(4)臨時表空間 temporary tablespaces
存儲臨時表的數據,包括用戶創建的臨時表,和磁盤的內部臨時表。對應數據目錄
下的 ibtmp1 文件。當數據服務器正常關閉時,該表空間被刪除,下次重新產生。

(5)Redo log
磁盤結構裏面的 redo log
redo log是用於異常恢復的
redolog大小是固定的,兩個48M的 文件,滿了之後會覆蓋老的數據

undo Log
undo Log 的數據默認在系統表空間 ibdata1 文件中,因爲共享表空間不會自動收縮,也可以單獨創建一個 undo 表空間
undo log :事務撤銷回滾日誌,記錄事務發生之前的狀態
undo log 是邏輯日誌,redo log是物理日誌(兩個日誌都屬於存儲引擎層)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章