MySQL-InnoDB拾遺

之前寫過一篇介紹MySQL中存儲引擎的文章MySQL之存儲引擎,在實際工作中,還是以InnoDB存儲引擎爲主,此文大部分爲InnoDB引擎中的概念拾遺。

體系模型

以上爲InnoDB存儲引擎大致上的體系結構。

其中後臺線程負責:刷新內存池中數據;將已修改的數據文件刷新到磁盤。
後臺線程又分爲Master Thread與IO Thread。Master Thread負責將緩衝池中的數據異步刷新到磁盤,保證數據一致性,包括髒頁的刷新,合併插入緩衝等。IO Thread則負責IO請求。

接下來是最核心的部分,內存池負責:緩存磁盤上的數據;在對磁盤文件的數據修改之前在這裏緩存;重做日誌(redo log)緩衝。內存池的結構又可劃分如下圖

內存池中最重要的結構顯而易見是緩衝池。

緩衝池:MySQL數據基於磁盤存儲,並將其中的記錄按照頁的方式進行管理。緩衝池是一段內存區域,來彌補磁盤速度較慢對性能的影響。對於讀操作,會將磁盤讀取到的頁放在緩衝池中,對於寫,首先修改在緩衝池中的頁,再定時刷新到磁盤。緩衝池的數據淘汰通過LRU算法進行管理。插入緩衝:對於非唯一輔助索引的插入或更新,不是每一次直接插入到索引頁中,而是先判斷插入的非聚集索引頁是否在緩衝池中,若在直接插入,若不在,先放入insert buffer中,之後多次操作合併,提高性能。關於緩衝池更詳細的介紹可以看58沈劍的這篇文章緩衝池,這次徹底懂了!

日誌文件

幾個重要的日誌文件:二進制日誌(MySQL層面),慢查詢日誌(MySQL層面),查詢日誌(MySQL層面),RedoLog(InnoDB層面),UndoLog(InnoDB層面)。查詢日誌包括慢查詢日誌就是SQL的執行記錄,一般通過它們來進行SQL優化,具體的不做介紹,着重關注不暴露給外面的幾種日誌。

BinLog:binlog是MySQL層面的日誌,記錄了數據的更改,用於故障恢復,主從複製等。binlog共有三種模式:STATEMENT,記錄SQL語句;ROW,記錄行更改;MIXED,默認採用STATEMENT,某些特殊情況下采用ROW,例如使用了臨時表。MySQL中有兩個跟binlog相關的重要參數
(1)binlog_cache_size,二進制日誌緩衝,當事務的記錄大於設定的binlog_cache_size時,mysql會把緩衝區中的日誌信息寫入一個臨時文件中。設置過大,會造成內存浪費。設置過小,會頻繁將緩衝日誌寫入臨時文件。
(2)sync_binlog,=0(默認)表示刷新binlog時間點由操作系統自身來決定,操作系統自身會每隔一段時間就會刷新緩存數據到磁盤,性能最好,但有可能丟失數據。=N,代表每N個事務提交會進行一次binlog刷新。N=1最安全,但性能較差。

RedoLog:InnoDB引擎層面,記錄了InnoDB的事務日誌,由兩部分組成,一是內存中的RedoLog緩衝,二是RedoLog文件。RedoLog記錄某數據塊被修改後的值,可以用來恢復未寫入data file的已成功事務更新的數據,保證事務的原子性和持久性。redo日誌與binlog日誌作用類似,但是前者爲InnoDB引擎特有,後者爲MySQL層面,所有的存儲引擎都會產生binlog。前者是物理格式,記錄的是每個頁的修改;後者是邏輯日誌,記錄的是對應的SQL語句。binlog只在事務提交完成後進行一次寫入,redolog則在事務進行中不斷寫入。同binlog一樣,redolog也有相關刷新內存緩衝的參數,innodb_flush_log_at_trx_commit控制重做日誌刷新到磁盤的策略,默認爲1,表示事務提交時進行一次fsync,爲0時表示每秒進行一次fsync(效率比前者高,但可能丟失一秒鐘的數據)

UndoLog:InnoDB引擎層面,記錄數據被修改前的值,可以用來在事務失敗時進行rollback,保證事務的一致性,回滾行記錄到某個特定版本,通常是邏輯日誌,根據每行記錄進行記錄。

 binlog 和 redolog的一致性問題:MySQL使用內部XA(兩階段提交)解決了 binlog 和 redo log的一致性問題。MySQL中的XA實現分爲:外部XA和內部XA;前者是指我們通常意義上的分佈式事務實現(因爲性能原因,生產分佈式事務不會採用此方案);後者是指單臺MySQL服務器中,Server層作爲TM(事務協調者),而服務器中的多個數據庫實例作爲RM,而進行的跨庫事務,也就是一個事務涉及到同一條MySQL服務器中的兩個innodb數據庫。同時內部XA也用來解決這兩種日誌的一致性問題。

B+樹索引

InnoDB採用B+樹作爲底層的索引結構,B+樹由二叉查找樹(左子樹鍵值小於根,右大於根),再由平衡二叉樹(子樹高度差最大位1),B樹演化而來,專爲磁盤等存儲設備設計。B+樹中所有記錄節點都是按鍵值得大小順序存放在同一層的葉子節點上,由各葉子節點指針進行連接。

B+樹索引分爲聚集索引和輔助索引,前者按照主鍵構造B+樹(當InnoDB表沒有主鍵時,會選擇使用唯一非空索引或者自動創建的6字節指針作爲主鍵。),同時葉子節點中存放的即爲整張表的行記錄數據,也將聚集索引的葉子節點稱爲數據葉,這個特性決定了索引組織表中數據也是索引的一部分。後者葉子節點中不包含行記錄的全部數據,葉子節點除了包含鍵值以外,索引行中還包含了一個書籤,該書籤(其實就是相應行數據的聚集索引鍵)用來告訴InnoDB哪裏可以找到與索引相對應的行數據。通過輔助索引獲取數據的流程是先找到對應書籤,再通過主鍵索引找到相應的數據

數據按照(a,b)的順序進行了存放,對於where a=xxx and b=xxx與where a=xxx都是可以使用(a,b)聯合索引的。而對於where b=xxx則不能使用該索引,葉子節點b值也非有序,不能使用索引。這也是索引的左前綴原則的原理。

InnoDB中的鎖

InnoDB中鎖可以分爲2個類型:
1.共享鎖 :允許事務讀一行數據
2.排它鎖:允許事務刪除或更新一行數據
鎖的兼容性:若事務T1獲得行R的共享鎖,T2可以立即獲取R的共享鎖,這種情況稱爲鎖兼容。排它鎖與任何類型的鎖都不兼容。

意向鎖:InnoDB支持多粒度鎖定,允許事務在行鎖和表鎖同時存在,爲了支持不同粒度上的加鎖操作,InnoDB使用了意向鎖的概念。意向鎖將鎖定的對象分爲多個層次,意味着事務希望在更細粒度上進行加鎖。例如,若需要對記錄R上排它鎖,那麼分別需要對數據庫、表、頁上意向鎖,最後再對R上排它鎖。其中任何一個部分導致等待,那麼該操作需要等待組粒度鎖的完成。例如若已經有事務對記錄所在的表進行了共享鎖,那麼再要對R上排它鎖,由於鎖的不兼容性,需要等待表鎖操作的完成。引進意向鎖後,系統對某一行加鎖時不必逐個檢查與下一級結點的封鎖衝突了,提升了性能。

一致性非鎖定讀:指InnoDB通過行多版本併發控制(MVCC)的方式來讀取當前數據庫中的數據。如果讀取的行正在執行update或delete操作,此時的讀取操作並不會等待行鎖釋放,而是讀取行的一個快照數據。MVCC只在可重複讀與讀已提交下工作。對於前者,總是讀取事務開始時的行數據版本,對於後者,總是讀取被鎖定行的最新一份快照數據。MVCC可以使得大部分讀操作都不用加鎖。
一致性鎖定讀:顯式的對數據庫讀取操作進行加鎖保證數據邏輯的一致性。對於select語句,支持兩種模式:select...for update; select...lock in share mode。前者對讀取的行記錄加一個排它鎖,其他事務不能對該行加任何鎖。後者對讀取的行記錄加一個共享鎖,其他事務可以加共享鎖但不能加排它鎖。這種方式也是我們熟知的數據庫悲觀鎖。

行鎖的3種算法:Record Lock:單個行記錄的鎖;Gap Lock:間隙鎖,鎖定一個範圍,但不包含記錄本身;Next-Key Lock:鎖定一個範圍,幷包含記錄本身。在可重複讀隔離級別下,InnoDB使用Next-Key Lock解決幻讀問題。

數據的複製

上面說過,MySQL通過binlog完成數據的複製。當數據庫做主從架構時,主庫把數據更改記錄到二進制日誌中,備庫將主庫的日誌複製到自己的中繼日誌(Relay log),備庫讀取中繼日誌的信息重放。MySQL會按照事務的提交順序而非執行順序來記錄binlog。備庫啓動一個IO線程跟主庫建立連接,然後在主庫上啓動一個二進制轉儲線程,該線程用於讀取主庫上二進制日誌中的事件,如果該線程追趕上了主庫,則會進入休眠狀態。備庫的SQL線程從中繼日誌中讀取事件並執行。若binlog爲statement模式,有些SQL可能略有問題,比如使用NOW函數,會有短暫延遲,若爲row模式,無法處理主庫修改表schema的情況。

主從同步延時的解決:半同步複製(當提交事務時,客戶端接收到查詢結束反饋前必須保證二進制日誌已經傳輸到至少一臺備庫上),並行複製(從庫開啓多個sql線程,並行重放),直連主庫(要求讀寫均走主庫),修改代碼邏輯(避免更新或插入後立即讀取),在事務中進行操作(在事務中讀寫都走主庫)

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