【Mysql技術內幕InnoDB存儲引擎】讀書筆記

1、存儲引擎

一、InnoDB引擎

設計目標是面向在線事務(OLTP)處理的應用。php

支持事務、行級鎖、經過多版本併發控制(MVCC)支持高併發、提供一致性非鎖定讀、next-key locking避免幻讀、主鍵彙集索引前端

二、MyISAM引擎

設計目標是面向OLAP應用。mysql

不支持事務、不支持行鎖、表鎖設計、支持全文索引算法

三、其餘存儲引擎

sql

2、InnoDB體系結構

一、線程模型

InnoDB存儲引擎是多線程模型,後臺有多個不一樣的線程,用於處理不一樣的任務。數據庫

  • Master Thread:核心線程,將緩衝池中的數據異步刷新到磁盤
  • IO Thread:負責io請求的回調處理
  • Purge Thread:負責undo頁的回收
  • Page Cleaner Thread:負責髒頁的刷新

1.一、Master Thread

內部由多個循環組成。包括主循環(loop),後臺循環(background loop)緩存

主循環每隔一秒的操做數據結構

  • 把日誌緩衝刷新到磁盤,即便這個事務尚未提交。很好的解釋了再大的事務提交時間也很短
  • 合併插入緩衝
  • 至多刷新n(可配置,自動調整,1.2版本以後)個髒頁到磁盤
  • 沒有用戶活動,切換到background loop

主循環每隔10秒的操做多線程

  • 合併最多5個插入緩衝
  • 緩衝日誌刷新到磁盤
  • 刪除無用的undo頁
  • 刷新髒頁到磁盤(超過70%,刷新100頁,沒超過70%,刷新10頁)

二、內存模型

2.一、緩衝池

InnoDB是基於磁盤的存儲系統,爲了彌補cpu和磁盤性能的差距,將從磁盤讀出的數據保存在內存中,下次讀取先從緩衝池中讀取。有數據更新也先更新緩衝池的數據,經過checkpoint機制寫回磁盤。緩衝池中包括索引頁、數據頁、undo頁、插入緩存、鎖信息等架構

2.二、緩衝池管理(LRU List)

最近作少使用算法,最頻繁使用的頁在List前端,最少使用的頁在List末尾。當緩衝池容量不足容納新數據時,先從尾部釋放數據頁。新數據插入在List的midpoint(List的5/8,對樸素LRU的優化,樸素LRU插入List頭部。避免大量一次性查詢把頻繁使用的頁刷出緩衝池)

2.三、髒頁管理(Flush List)

當數據被更新,緩衝池中的數據首先被更新,修改以後的頁稱爲髒頁。髒頁會保存到Flush List中,經過checkpoint機制把髒頁數據寫回磁盤

2.四、重作日誌(redo log)緩衝

首先把重作日誌信息存入緩衝區,而後按照必定頻率同步到重作日誌文件中。如下三種狀況都會觸發重作日誌緩存同步到重作日誌文件:

  • Master Thread 每隔一秒刷新
  • 每一個事務提交時
  • 重作日誌緩衝池容量達到閾值,通常是1/2

2.五、check point技術

爲了防止宕機致使事務未提交信息丟失,在事務提交時,先把數據保存到重作日誌(redo log)中,再修改頁。保證了持久性(D)

發生宕機,重啓以後自動從重作日誌中恢復數據。

可是這裏有如下問題:

  • 重作日誌過大,宕機重啓恢復數據太慢
  • 重作日誌不能無限擴容,須要循環利用
  • 重作日誌不可用怎麼辦

check point就是爲了解決這些問題:

  • 縮短數據庫恢復時間
  • 重作日誌不可用,刷新髒頁
  • 緩衝池不夠用,將髒頁刷新到磁盤

check point觸發時機:

  • Master Thread check point。每隔一秒觸發一次
  • LRU List check point。保證LRU List中有100個空閒頁,若是清理的頁中有髒頁,觸發check point 強制刷新髒頁數據到磁盤
  • Dirty Page too mush check point。髒頁太多,超過閾值,觸發check point 強制刷新髒頁數據到磁盤

三、關鍵特性

3.一、插入緩存

(1)爲何須要插入緩存?

咱們知道索引分爲彙集索引和非彙集索引。

彙集索引通常是自增的惟一id,頁中的數據記錄按順序存放,寫入的時候不須要隨機讀取其餘頁中的數據,寫入速度很快(若是用UUID做爲主鍵,寫入速度會很慢,每次寫入都須要隨機讀)

實際應用中,一張表每每還有非彙集索引的存在。非彙集索引葉子節點的插入不是順序的,須要離散的訪問非彙集索引頁,隨機讀取致使了插入數據的性能降低。插入緩存就是爲了優化這種場景下的插入速度

(2)什麼場景會觸發插入緩存?

  • 索引是輔助索引
  • 索引不是惟一索引

對於非彙集索引的插入,會先判斷非彙集索引頁是否在緩衝池中,若是在緩衝池中,直接插入索引頁,若是沒在,先放入到insert buffer對象中,而後再以必定的頻率把insert buffer中的數據和非彙集索引的葉子節點進行數據合併

(3)實現原理

insert buffer 的數據結構也是B+樹,有記錄要插入的時候,會對記錄進行封裝,按照記錄的插入順序進行編號,是順序寫入

3.二、兩次寫

(1)爲何須要插入兩次寫?

若是InnoDB正在寫入某個頁的數據到磁盤,正好寫了一部分的時候宕機了。這種狀況稱爲部分寫失效,會致使數據丟失

(2)實現原理

double write由兩部分組成。一部分是double write緩衝,一部分是物理磁盤連續共享空間。在刷新髒頁數據的時候,先複製一份髒頁數據到兩次寫緩存中,在順序寫入共享磁盤中(由於是順序寫性能影響不大)。最後寫入數據存儲磁盤中(離散寫)

3.三、自適應hash索引優化

hash是很是快的查詢方式,時間負責度爲O(1)。而B+樹的查找次數取決於樹的高度。

若是一個頁被頻繁的訪問,並且訪問模式也相同(聯合索引使用最左原則)。會自動針對這頁數據根據緩衝池中的索引創建Hash索引提升查詢速度

3.四、異步IO

能夠在發出一個IO請求後,在發出另外的IO請求,不必等待上一次的IO請求處理完成。把所有IO請求都發出,等待全部IO操做的完成,這就是AIO(Aysnc IO)

 3、文件

MySQL據庫和InnoDB存儲引擎有不少類型的文件,每種文件用處不一樣。主要有參數文件、sokcet文件、pid文件、日誌文件、表結構文件、存儲引擎文件

一、日誌文件

  • 錯誤日誌:記錄啓動運行以及關閉遇到的錯誤信息
  • 查詢日誌:記錄全部的查詢記錄
  • 二進制文件(binlog):記錄全部的數據更改記錄。用於數據恢復和數據複製。事務中未提交的二進制日誌會存放到緩衝中,等事務提交時直接將緩衝中的日誌同步到二進制文件中。經過配置能夠指定寫緩衝多少次以後同步到磁盤,若是值設置大於1,當發生宕機時可能會丟失數據
  • 慢查詢日誌:查詢時間超過指定閾值的記錄

二、InnoDB存儲引擎文件

  • 表空間文件:存儲數據
  • 重作日誌文件:存儲事務日誌

4、表

一、索引組織表

InnoDB中,表數據都是按照主鍵順序組織存放的。每張表都有主鍵,若是沒有顯示的定義主鍵,會把惟一索引做爲主鍵。若是惟一索引也沒有,會自動建立6字節大小的指針做爲主鍵

二、存儲結構

全部數據都存放在表空間中,表空間又由段、區、頁組成

  • 段:表空間由各個段組成。段的管理由引擎自身完成
  • 區:每一個區大小爲1M,由連續頁組成
  • 頁:磁盤管理的最小單位,默認每一個頁大小16k
  • 行:數據是按行進行存放的,一個頁最多存放16k/2-200=7992行

5、索引

數據庫的索引結構是B+樹,高度通常在2-4層,一次查找只須要2-4次的io。索引分爲彙集索引和非彙集索引

一、彙集索引

按照每張表的主鍵構建的B+樹,葉子節點中存儲着整張表的行記錄數據,每一個葉子節點經過雙向鏈表進行鏈接。由於實際的數據頁只能按照一個彙集索引進行排列,每張表只能擁有一個彙集索引

彙集索引對於主鍵的範圍查找和排序查找速度很是快。

二、非彙集索引(輔助索引)

葉子節點不包含行記錄的所有數據,葉子節點只存儲了鍵值和指向彙集索引的書籤

三、索引建立和刪除

3.一、Online Scheme Change(OSC)

在線架構改變,經過php腳本實現,在索引的建立或刪除過程當中,能夠有讀寫事務對錶進行操做。過程以下:

  • 建立和原表結構同樣的新表
  • 對新表進行alter table操做
  • 建立臨時表
  • 對原表添加觸發器,把新產生的數據變化同步到臨時表
  • 把原表的數據同步到新表
  • 將臨時表的數據同步到新表
  • 對新表建立輔助索引
  • 再次回放臨時表的數據到新表
  • 新表和舊錶交換名字rename

3.二、mysql5.6開始支持online DDL

經過新的alter語法,能夠選擇索引的建立方式

四、什麼樣的數據適合建立輔助索引?

Cardinality表示索引中惟一值的數據的估計值(不是實時更新,使用採樣法延遲更新),應儘量接近表中數據總行數。

五、聯合索引

聯合索引也是一棵B+樹,不一樣的是索引鍵值的數量大於等於2。聯合索引的第二個好處是已經對第二個鍵值作了排序處理,減小了一次額外的排序操做

6、鎖

一、MyISAM引擎

MyISAM引擎的鎖是表鎖設計,併發狀況下讀沒有問題,可是寫的性能會比較低

二、InnoDB引擎

2.一、鎖的類型

  • 共享鎖(S Lock)
  • 排它鎖(X Lock)

2.二、一致性非鎖定讀

實現原理是經過MVCC機制實現,若是讀取的行正處於update或delete中,讀操做不會去等待行上X鎖的釋放,而是去讀取行的快照數據。

一致性非鎖定讀能夠極大的提升併發性能

不一樣的事務隔離級別,讀取的快照版本是有差異的

  • 讀已提交隔離級別,老是讀取最新的快照版本。可能會產生幻讀
  • 可重複讀隔離級別,老是讀取事務開始後第一次讀取的快照版本。能夠避免幻讀的產生

2.三、一致性鎖定讀

默認配置下,採用可重複讀的隔離級別,讀取數據採起的是一致性非鎖定讀。

可是某些場景下須要對讀取操做加鎖來保證嚴格的數據一致性,這時候能夠顯式的對讀取的記錄進行加鎖:

  • select *** for update(對讀取記錄加X鎖)
  • select *** lock in share model(對讀取記錄加S鎖)

2.四、鎖的算法

  • record lock:單個記錄的鎖。
  • gap lock:間隙鎖,鎖定一個範圍,不包括記錄自己
  • next-key lock:gap lock+record lock

默認隔離級別(可重複讀)下,默認加的是next-key lock(爲了解決幻讀問題),當索引中含有惟一屬性時,會降級爲record lock。

在讀已提交隔離級別下,加的是record lock

舉個例子:

如今表z,有a,b兩列,a是主鍵索引,b創建輔助索引。如今記錄以下:(1,1)(3,1)(5,3)(7,6)(10,8)

select * from z where b=3 for update

由於鎖是經過對索引加鎖實現的。因此這裏須要對主鍵索引和輔助索引加鎖,主鍵索引加的鎖是record lock,輔助索引加的鎖是next-key lock,鎖定範圍是(1,3)、三、(3,6)

相關文章
相關標籤/搜索