首頁 > 軟體

MySQL學習之InnoDB結構探祕

2023-04-02 06:02:50

InnoDB架構圖

記憶體結構

Buffer Pool

why buffer pool ?

InnoDB是基於磁碟儲存,其儲存的最基本單元是頁,大小為16KB。而CPU和磁碟之間速度相差懸殊,所以通常使用記憶體中的緩衝池來提高效能。

what is buffer pool ?

緩衝池裡主要快取了:

  • data page 資料頁

  • index page 索引頁

  • insert buffer

    • what is insert buffer ?

      插入緩衝, 用於將多個insert操作合併成一個,減少IO開銷,提高插入效能。只能用在非唯一的輔助索引

    • why ?

      插入主鍵索引時是順序的,不需要隨機IO,速度會很快。但對於非唯一的輔助索引,插入的葉子節點是分散的,需要離散的存取索引頁。

    • how does it work ?

      對非唯一輔助索引,索引的修改並非實時更新索引樹的葉子節點,而是把若干個對同一頁的更新操作快取起來,合併為一次操作,從隨機IO轉為順序IO,減少IO次數,提高寫入效能。

      流程:

      先判斷要更新的索引頁,是否在緩衝池中

      1. 若是,直接插入
      2. 若不是,則存入Insert Buffer,後交給Master Thread 來進行合併
    • extend :

      一開始只針對INSERT操作,所以叫insert buffer,後來能夠支援 INSERT/UPDATE/DELETE,所以改叫change buffer了

    若是唯一索引,在插入時需要檢查索引列的值是否存在,故在修改索引之前,需要把相關的索引頁讀出來,去判斷是否唯一,這時Insert Buffer就失效了。

  • lock info 鎖資訊

  • data dictionary 資料字典(主要包括了一些後設資料資訊,如表結構資訊)

  • adaptive hash index 自適應hash索引,InnoDB為熱點頁建立的索引,用以提高查詢效率

how does it work ?

資料庫的讀操作,會先判斷欲讀取的頁是否在緩衝池中,若是,則命中緩衝;否則,從磁碟上讀取,並將讀取到的頁放入緩衝池。

資料庫的寫操作,是先修改緩衝池中的頁,再以一定頻率,將緩衝池重新整理到磁碟。將資料從緩衝池重新整理到磁碟,是通過一種checkPoint的機制完成的

practice ?

可通過設定引數innodb_buffer_pool_size來設定緩衝池大小

可以看到本機的mysql緩衝池大小為134217728B,換算後是128MB

Redo log buffer

why redo log buffer ?

當緩衝池中的頁是髒頁(修改資料時,是先修改緩衝池中的資料,此時緩衝池中的資料和磁碟上不一致,稱為髒頁)時,需要通過某種機制將髒頁重新整理到磁碟。若緩衝池中一有資料頁發生改變,就馬上重新整理磁碟,效率會很低。所以InnoDB採用Write Ahead Log策略,事務提交時,先將redo log寫入磁碟,這樣就認為髒頁已經寫入到磁碟了。之後再通過checkpoint機制擇時將髒頁真正寫入磁碟,髒頁真正寫入磁碟後,就可以刪掉對應的redo log了。若髒頁還沒寫入磁碟,發生了宕機,則由於redo log已經成功寫入磁碟,故可以通過redo log進行資料恢復。

redo log保證了事務的永續性。寫redo log時,先將redo log放入redo log buffer,再將redo log按一定策略重新整理到磁碟,這是通過innodb_flush_log_at_trx_commit引數來設定的,引數名在不同的mysql版本或許有不一樣,通過如下命令可以檢視:

show variables like 'innodb_flush%';

innodb_flush_log_at_trx_commit引數設定有3個取值:0,1,2 其含義如下圖所示

innodb_flush_log_at_trx_commit屬性可以控制每次事務提交時InnoDB的行為。當屬性值為0時,事務提交時,redo log被寫到redo log buffer,然後等待主執行緒按時寫入;當屬性值為1時,事務提交時,會將redo log寫入檔案系統快取,並且呼叫檔案系統的fsync,將檔案系統緩衝中的資料真正寫入磁碟儲存,確保不會出現資料丟失;當屬性值為2時,事務提交時,也會將紀錄檔檔案寫入檔案系統快取,但是不會呼叫fsync,而是讓檔案系統自己去判斷何時將快取寫入磁碟。

當引數值為0時,寫入效率最高,但是資料安全最低;引數值為1時,寫入效率最低,但是資料安全最高;引數值為2時,二者都是中等水平。一般建議將該屬性值設定為1,以獲得較高的資料安全性,而且也只有設定為1,才能保證事務的永續性。

redo log buffer的大小可以通過innodb_log_buffer_size去控制

​Double Write Buffer

雙寫緩衝,和磁碟檔案中系統表空間裡的Double Write Segment一起解決了頁的部分寫入失效問題。

MySQL資料庫IO的最小單位是16KB,檔案系統(File System)IO的最小單位是4KB,磁碟IO的最小單位是512B。(具體的大小可能有差異,但意思就是這麼個意思,MySQL的基本儲存單位和檔案系統的基本儲存單位大小不一致)。由於MySQL的1個單位(頁),相當於檔案系統中的4個單位,在將記憶體中的髒頁重新整理到磁碟時,一頁會分4次進行寫入,若在寫入過程中發生意外情況,比如斷電,宕機,則可能成功寫入2次,即寫入了8KB,那另外8KB還是舊的資料,這叫做部分寫失敗,導致這一頁的資料,一半是新的,一半是舊,資料不完整,成為壞頁,最終資料不一致。此時redo log 也無能為力,因為redo log記錄的是對物理頁的修改操作,此時頁本身已經損壞,再對損壞的頁應用修改操作,也無法恢復為完整資料。

Double Write就是為了解決這個問題。

Double Write 由2部分組成

  1. 記憶體中的Double Write Buffer(2M)
  2. 磁碟共用表空間中的Double Write Segment(2M)

它的工作機制是這樣的

  1. 當觸發了髒頁重新整理時,髒頁並不直接寫入磁碟檔案,而是先拷貝到記憶體中的Double Write Buffer
  2. 接著將Double Write Buffer,分兩次寫入到共用表空間中,每次寫1MB
  3. 最後再將Double Write Buffer中的髒頁資料,寫入到實際的各個表空間裡,寫入完成後,即標記對應的Double Write 資料可被覆蓋

若發生意外宕機等情況,先從共用表空間中取出Double Write資料,複製到表空間中,再應用redo log,這樣即完成了資料恢復。

優點

提高了資料的可靠性

缺點:

由於Double Write 實際是一個物理檔案,即是一個file,它會導致作業系統進行更多的fsync刷盤操作,所以它會降低mysql的效能。然而,double write buffer往磁碟寫的時候是順序寫入,效能很高。

可以關閉Double Write的場景

  1. 頻繁的DML
  2. 不擔心資料損壞和丟失
  3. 系統主要負載集中在寫操作

簡單來說,就是加了一箇中間層,先將要寫入磁碟的資料,暫存到一箇中繼站,成功存到中繼站之後,再進行實際的寫磁碟,寫完磁碟後,再告訴中繼站說,剛才暫存到你那裡的資料我已經落盤了,你可以把它標記為無用資料了。相當於拿這個中繼站做了資料的保險。如果在實際寫磁碟時,發生意外,那麼損壞的資料塊,我可以從中繼站那裡拿到一份完整的拷貝,保證了資料的完整性。

磁碟檔案

表空間

  • 系統表空間(共用表空間 ibdata1)

    它是被多個表共用的,可以通過innodb_data_file_path引數對系統表空間進行設定

# 格式
innodb_data_file_path = datafile1[,datafile2]
# 可以指定多個檔案,共同組成系統表空間
innodb_data_file_path = /db/ibdata1:1000M;/db/ibdata2:1000M
# 設定了這個引數後,所有基於InnoDB的表,都會被儲存到系統表空間
    • 資料字典(各種後設資料,如表結構的定義)
    • Double Write Buffer
    • Insert Buffer (Change Buffer)
    • undo log
    • 在系統表空間建立的表資料,索引資料
  • 使用者表空間(獨立表空間 ibd)

# 開啟獨立表空間
innodb_file_per_table = 1
# 設定這個引數後,每個表都是一個單獨的 .ibd 檔案

我本機的MySQL是預設開啟了這個引數

所以都是一個表一個ibd檔案

但是獨立表空間裡,只存了資料,索引,插入緩衝bitmap。其餘的資訊還是存在系統表空間。

重做紀錄檔檔案

redo log :

一般InnoDB的資料目錄下,會有2個名為ib_logfile0和ib_logfile1的檔案,這就是redo log檔案

每個InnoDB引擎的表至少要有1個重做紀錄檔組(group),一個group下至少有2個重做紀錄檔。為了得到更高的可靠性,使用者可設定多個映象紀錄檔組

InnoDB根據checkpoint對2個檔案進行迴圈寫入。

可通過innodb_log_file_size設定redo log的大小。若設定太大,資料丟失時,恢復可能要花很長時間;若設定太小,則會導致checkpoint進行頻繁檢查,並將髒頁重新整理到磁碟,導致效能抖動。

重做紀錄檔的落盤機制在上面的redo log buffer裡已經說明,簡單總結起來就是2個機制:Write Ahead Log + Force Log at Commit

這兩者保證了事務的永續性

一次寫操作的事務流程如下圖所示:

若發生了崩潰,則恢復資料的過程如下圖所示:

binlog是維護在SQL Layer層的,故不包含在InnoDB中。

以上就是MySQL學習之InnoDB結構探祕的詳細內容,更多關於InnoDB結構探祕的資料請關注it145.com其它相關文章!


IT145.com E-mail:sddin#qq.com