mysql innodb存儲引擎學習 (2)

接上:https://blog.csdn.net/qq_32250495/article/details/99099152

四、表
4.1 索引組織表。在innoDB中,表都是按照主鍵順序組織存放的,這種存儲方式的表稱爲索引組織表。innoDB會按照如下方式選擇或創建主鍵:
    首先判斷表中是否有非空的唯一索引,如果有則爲主鍵。如果沒有則innnoDB自動創建一個6字節大小的指針。

4.2 innoDB邏輯存儲結構
    從邏輯上來看,innodb所有的數據都被邏輯的放在一個空間中,稱爲表空間。表空間又由段、區、頁組成。
    表空間:innodb有一個共享的表空間ibdata1,如果配置一下參數,可以使每張表都有一個表空間,用於存放數據、索引和插入緩衝的bitmap頁,其他數據,如果回滾段、插入緩衝索引、系統事務、二次寫緩衝依然在共享表空間中。
    段:表空間由段組成,常見的段有數據段,索引段,回滾段。由於innoDB的表示索引組織的,所以數據就是B+樹所以的葉子節點,索引就是B+樹的非葉子節點。
    區:區是由連續頁組成的空間。每個區大小爲1MB,爲了保證區的連續性,innoDB一次從磁盤申請4-5個區。默認情況下innoDB存儲引擎頁的大小爲16KB,即一個區由64個連續的頁。innoDB段在初始分配的時候只有32個碎片頁,只有在超過該大小後才連續分配。
    頁:頁是innoDB磁盤管理的最小單位。
    行:頁中按照行存放數據,每個頁最多存儲7992行記錄。

4.3 行格式
    在老的版本中使用稱爲 antelope(Compact, Redundent)的文件格式,在新版本開始使用barracuda(Compressed,Dynamic)的文件格式
    Compact行記錄格式:變長字段長度列表(1-2字節),null標誌位(1字節),記錄頭信息(5字節),事務ID列,回滾指針列,列數據。回滾指針列記錄了undo log中的數據。頁中所有行都通過鏈表串聯起來。
    Redundent行記錄格式:變長字段長度列表。記錄頭信息(6字節),列1,列2... 該格式是數據庫5.0之前的版本格式。

    新的記錄格式對於blob數據完全採用行溢出的方式。數據頁中只存放20個字節的指針。Compressed格式會壓縮blob頁。

4.4 行溢出數據
    根據行格式定義可知,一行記錄可以創建65535字節大小。但是數據頁爲16K,無法存儲這麼大的數據,當數據發生行溢出時候,數據存放在blob頁中。而真正的數據頁只保存了768字節的前綴數據,之後是偏移量,指向行溢出頁。
    innoDB存儲引擎表是按照B+樹組織的,所以每頁數據中至少包含2條記錄。如果頁中足夠存放兩條數據,則不會存放到blob頁中。

4.5 頁結構
    數據頁可由7個部分組成。
    File Header。 文件頭用來記錄頁的一些頭信息。佔用38個字節。checksum(4),page_offset(4)表空間中的頁偏移,page_prev(4)上一個頁,page_next(4)下一個頁,page_lsn(8)最後被修改的日誌序列。page_type(2)頁類型。page_file_flush_lsn(8)文件至少被更新到該lsn值。space_id(4)記錄該頁輸入那個表空間
    Page Header。 記錄頁的狀態信息。佔用56字節。page_n_dir_slots(2)頁目錄中的槽數量。page_heap_top(2)堆中第一個記錄的指針。page_n_heap(2)堆中記錄數。page_free(2)可重用空間的首指針。page_garbage(2)已刪除的字節數,即delete flag爲1的行記錄總大小。page_last_insert(2)最後插入記錄的位置。page_direction(2)最後的插入方向。page_n_direction(2)一個方向連續插入的記錄數。page_n_recs(2)頁中記錄的數量。page_max_trx_id(8)修改當前頁的最大事務ID.page_level(2)當前頁在索引中的高度。page_index_id(8)當前頁輸入那個索引。page_btr_seg_leaf(10). page_btr_seg_top(10)段頭。
    Infimun和Supremun。innoDB中,每個數據頁有2個虛擬的行記錄,用來限定記錄的邊界。指定該頁中比主鍵最小的值和可能的最大主鍵值。
    User Record。實際行數據。
    Free Space。空閒空間,將空閒空間通過鏈表組織起來
    Page Directory。頁目錄中存放了記錄的相對位置。該目錄是一個稀疏目錄,所以二叉查找爲一個近似結果,因此Innodb需要通過行記錄的nextRecord繼續查找。
    File Trailer。爲了檢測頁是否已經完整的寫入磁盤。通過8字節的 page_end_lsn字段校驗與file head中的checksum和lsn保證頁的完整性. innoDB在從磁盤讀取一個頁就是檢測頁的完整性。
    
五、索引與算法
5.1 索引分類
    聚集索引:就是按照每張表的主鍵構造一顆B+樹,同時葉子節點中存放的即爲整張表的行記錄數據。聚集索引的這個特性決定了索引組織表中的數據也是索引的一部分,每個數據頁都通過一個雙向鏈表來進行鏈接。由於實際的數據頁只能按照一顆B+樹進行排序,所以每張表只能有一個聚集索引。通過聚集索引,查詢優化器能很快找到某一頁的數據,或者進行範圍掃描。
    輔助索引:也稱爲非聚集索引,輔助索引的葉子節點除了包含鍵值以外,還包含了相應行數據的聚集索引件。查詢通過輔助索引找到聚集索引鍵後,再通過聚集索引鍵查詢數據。
5.2 B+樹的分裂
    如果從節點的中間鍵進行分裂,由於插入時按照主鍵順序進行的,將導致一半的頁空間浪費。爲了避免這種情況,innoDB在page head中加入幾個狀態字段指導分裂的位置。
    page_last_insert:最後插入鍵的位置
    page_direction: 最後插入的方向
    page_n_direction:最後連續插入n個記錄的方向。
    B+樹分裂規則:以當前插入記錄的前一條記錄爲軸,如果軸後面有3條記錄,則分裂點爲該位置,否則分裂點爲插入點本身。
5.3 索引管理
    cardinality:索引中唯一鍵的估計值。cardinality的值應該儘可能接近於1,這樣表示其可選擇性越高,建立索引的優勢更大。否則,根據該索引查詢依舊需要進行範圍掃描。cardinality的值並不是實時更新的,如果需要更新改值需要使用analyze table命令。在非高峯期做該命令,可以讓優化器和索引更好的工作。
    cardinality更新方式:1. 表中1/16的數據已經發生過變化 2.stat_modified_counter > 20億。 當出現這兩個條件的時候發生更新,通過採用統計8個頁中不同的記錄數/8 然後乘總頁數來給出cardinality的預估值。
    舊索引創建:1 創建一張新的臨時表(具有新的索引結構) 2. 把原表數據導入臨時表 3 刪除原表。 4 重命名原表
    快速索引創建:只針對輔助索進行重建,需要對錶加S鎖
    Online DDL:將DML操作日誌寫入到緩存中,待完成索引創建後再將重做應用到表上。,以此達到數據一致性的目的。
5.4 索引覆蓋
    innoDB支持從輔助索引中就可以查詢到記錄。由於輔助索引不包含整行記錄信息,估其大小原小於聚集索引。如果走輔助索引就能拿到查詢的列數據或者進行統計計算,則優化器會先選擇輔助索引。
5.5 索引提示
    通過索引提示來指示查詢優化器強制使用某個索引,同時優化器不需要分析使用哪個索引。 
    use index(`a`): 告訴優化器使用哪個索引,但是不強制
    force index(`a`): 強制優化器使用某個索引
5.6 multi_range read優化
    MRR將對輔助索引的隨機訪問,轉化爲較爲順序的數據訪問,適合於範圍類型的查詢,可以通過參數 optimizer_swith='mrr=on' read_rnd_buffer_size控制緩存區的大小
    1. 將查詢得到的輔助索引鍵值存放在一個緩存中。
    2. 將緩存中的鍵值根據rowID進行排序
    3. 按照rowID排序的順序來訪問實際的數據文件
5.7 index condition pushdown (ICP)優化
    將查詢條件推到索引查詢的時候,取出索引的同時就進行where條件的過濾

六、鎖
6.1 鎖的類型
    共享鎖S:允許事務讀取一行數據,共享鎖可以與共享鎖兼容
    排它鎖X:允許事務更改一行數據,排它鎖不兼容任何鎖。
    意向鎖:innoDB的意向鎖是一個表級鎖,設計目的主要是爲了在一個事務中揭示下一行將被請求的鎖類型。
    意向共享鎖:表示事務想要獲得一張表中某幾行的共享鎖
    意向排他鎖:表示事務想要獲得表中某幾行的排它鎖
6.2 一致性讀取方式
    一致性非鎖定讀-MVCC:多版本併發控制,事務讀取數據無需訪問鎖,事務在讀取數據的時候總是讀取事務開始時候的最新版本。這個版本即爲undo log
    一致性鎖定讀:讀取的時候進行加鎖
    select lock for update加x鎖
    select lock in share mode加s鎖 
    自增長與鎖:每個表都擁有一個自增長計數器,在自增計數器的時候,如果插入的時候行數確定(simple insert),則需要通過mutex進行併發控制。如果函數不確定(insert select)則會上表鎖。
6.3 鎖的算法
    1. record lock 單個行記錄鎖,如果表沒有添加索引,則鎖住隱式主鍵
    2. gap lock 間隙鎖,鎖定一個範圍,但不包括記錄本身
    3. next-key lock:鎖定一個範圍且鎖定記錄本身。innoDB對於行的查詢都是使用這種鎖定算法。例如:對於一個索引有 10,11,13,20這四個值,則[-無窮,10],[10,11],[11,13],[13,20],[20,+無窮]這幾個區間全部被鎖定
    next-key lock降級,對於唯一索引,如果查詢條件唯一,則可降級爲行鎖。這樣對於某些查詢可以提高性能。但是對於輔助索引則不可降級,這樣確保走輔助索引查詢時事務不會出現幻讀。
    備註: innoDB在可重複讀隔離級別下開啓了,間隙鎖。所以在該級別就可以避免幻讀問題。而oracle數據庫必須要序列化級別才能避免幻讀。 總的來說事務的隔離級別,對於發生回滾和範圍數據查詢才具有意義。
6.4 鎖升級
    鎖升級:即由行鎖升級爲表鎖或者頁鎖。由於innoDB以頁的維度管理鎖資源,並且通過位圖的方式記錄鎖。所以不存在鎖升級的問題,且鎖維護的開銷也很小。

七、事務
7.1 事務特性
    原子性:一組操作要麼都執行成功,要麼都不執行。
    一致性:將數據庫從一種一致的狀態變爲下一種一致的狀態。
    隔離性:事務之間是相互隔離。
    持久性:事務一旦提交,其結果就是永久性的
7.2 事務種類
    扁平事務:從開始執行事務到提交,或者回滾
    帶有保存點的扁平事務:允許事務回滾到同一事務的較早的一個狀態。保存點用來記錄事務當前的狀態
    鏈事務:一個事務提交後自動開啓下一個事務,該類型事務回滾只能回滾到前一個保存點
    嵌套事務:頂層事務提交纔出發子事務的提交。任一子事務回滾,將導致整個事務回滾
    分佈式事務:通常是一個在分佈式環境下運行的扁平事務,因此需要根據數據所在位置訪問網絡中的不同節點。
7.3 事務的實現
    redo log:是物理日誌,記錄數據頁的修改記錄。事務更新數據過程中,會在內存的重做日誌緩衝中寫redo log,事務提交的時候需要調用fsync將內存緩衝中的日誌刷新到磁盤中。
    innodb_flush_log_at_trx_commit參數控制刷磁盤的方式:默認1 事務提交立馬執行一次刷盤;2 事務提交將日誌寫入文件系統緩衝 0 只由master線程刷新磁盤
    log block:重做日誌都是以512字節進行存儲的,稱之爲重做日誌塊。由於重做日誌塊大小和磁盤扇區大小一樣,因此日誌的寫入可以保證原子性。
    log block header:12字節。用來記錄數據長度,起始偏移等信息
    log block tailer:8字節。 
    log body:492字節 
    重做日誌文件中存儲的就是log buffer中保存的log block,innodb根據一定的規則將log block中的內容刷新到磁盤。1 事務提交時候 2. log buffer中有一半的內存空間已被使用 3 log checkpoint
    備註:log group的第一個重做日誌文件前2KB需要保持log file head和 checkpoint等信息,並經常更新,所以對重做日誌文件的寫入並不是完全順序的。
    LSN:表示重做日誌寫入的總字節量,該值不僅記錄在redo log中而且寫在所有頁頭中,表示該頁最後刷新時LSN的大小     
    恢復:由於redo log文件記錄了checkpoint的信息,所以在恢復的時候僅須要恢復checkpoint開始的日誌部分,innodb將順序讀取日誌文件,並行應用重做日誌
    undo log:undo log用於邏輯性的記錄數據的更改記錄(對原操作進行反向操作),在事務回滾的時候利用該日誌恢復到最初的樣子。 undo log存放在共享表空間的回滾段中,undo log的產生也會寫redo log
    undo存儲管理。事務在寫undo log時候需要在回滾段中申請undo頁,在事務提交時將undo log放入列表中(有purge線程來判斷是否刪除該頁),供purge操作,並判斷該頁是否可以重用。
    insert undo log:插入操作產生的log,在事務提交後直接刪除,無需purge
    update undo log:更新產生的log,事務提交後需要提交到鏈表中,供purge線程檢查
    purge線程:history list按照事務提交的順序將undo log進行組織,在innodb中由於undo log的重用,一個undo page可能有多個事務的日誌。在執行purge過程中,innodb先從history list中找到第一個需要清理的記錄。如果找的的頁中有其他事務的記錄,則嵌套去檢查並清理該事務記錄,在該頁清理完成後,繼續去讀history list。innodb這樣設計主要爲了減少隨機讀取操作。
7.4 組提交
    事務在提交的時候需要進行兩個過程:1 將日誌寫入重做日誌緩衝redo log block 2 調用fsync進行刷盤。 組提交就是一次fsync可以將多個事務的日誌刷新到磁盤中
    由於在主備模式下,事務提交需要同時刷二進制日誌和重做日誌,需要確保這兩個過程是原子的。爲了保證二進制日誌的寫入順序,和引擎層的提交順序一致。需要使用prepare_commit_mutex鎖來使引擎層同步順序提交事務。以免通過在線恢復的時候數據丟失。但是由於fsync需要同步,導致了組提交帶來的性能優勢損失
    在數據庫5.6中將事務的提交過程分爲幾個步驟來完成
    1. flush階段,將每個事務的二進制日誌寫入內存中
    2. sync階段,將內存中的二進制日誌刷新到磁盤
    3. commit階段,leader根據順序調用存儲引擎層事務的提交。
    blgc事務提交方式:將提交的事務順序放入一個隊列中,隊列中的第一個事務稱爲leader,其他事務爲follower,leader控制follow的行爲。

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