基於《MySQL技術內幕(InnoDB存儲引擎)第2版》一書整理的筆記
注:使用Sublime Text編輯的,博客顯示效果並不理想,可粘貼到本地使用Sublime Text打開閱讀。
存儲引擎是基於表的,而不是基於數據庫的。
MyISAM引擎不支持事務,不支持行級鎖,支持全文索引,主要面向一些OLAP數據庫應用。MyISAM的緩衝池只緩存索引文件,不緩存數據文件。
InnoDB後臺線程:
Master Thread:
日誌緩衝刷新到磁盤,刷新髒頁到磁盤,合併插入緩衝,UNDO頁的回收。
IO Thread:
InnoDB大量使用AIO來處理I/O請求。InnoDB1.0之前版本有4個IO Thread,分別是read,write,insert buffer,log IO Thread。InnoDB1.0.x版本開始,read thread和write thread分別增大到了4個。
Purge Thread:
事務被提交後,其所使用的undolog可能不再需要,因此需要Purge Thread來回收已經使用並分配了的undo頁。
Page Cleaner Thread:
InnoDB1.2x版本後引入的,將髒頁刷新放到單獨的線程中執行,減輕Master Thread的工作,降低用戶查詢的延遲。
InnoDB中內存:
緩衝池:
InnoDB的存儲引擎是基於磁盤的,由於CPU的磁盤的速度差異很大,使用內存緩衝池來彌補磁盤速度較慢對數據庫的影響。在數據庫進行讀操作時,首先將磁盤讀到的頁放入緩衝池中,下次讀到相同的頁時,判斷該頁是否在緩衝池中,若在,則直接讀取緩衝池中數據,否則從磁盤讀取。
頁的修改操作通過CheckPoint機制刷新回磁盤。
緩衝池的數據頁類型有:索引頁,數據頁,undo頁,插入緩衝,自適應哈希索引,InnoDB存儲的鎖信息,數據字典信息等。
InnoDB緩衝池基於優化的LRU算法進行管理。在LRU列表中加入midpoint,新讀取到頁不是放在LRU列表的首部,而是放在midpoint位置,這是因爲某些sql操作,如索引或數據的掃描操作,會訪問表中的許多頁,這些頁通常來說只在這次查詢中需要,而不是熱點數據,如果放在LRU首部很可能導致熱點數據被從LRU列表刪除。還需要引入一個參數來表明數據頁到達mid位置後需要多久才能被放到LRU熱端。
重做日誌緩衝:
重做日誌緩衝刷新到磁盤的情況:
Master Thread每秒鐘將重做日誌緩衝刷新到重做日誌文件。
每個事務提交時會將重做日誌緩衝刷新到重做日誌文件。
當重做日誌緩衝池剩餘空間小於1/2時,重做日誌緩衝刷新到重做日誌文件。
額外的內存池
爲了避免數據丟失,當前事務數據庫系統普遍採用了 Write Ahead Log策略,先寫重做日誌,再提交事務。
CheckPoint主要解決以下幾個問題:
1縮短數據庫的恢復時間
2緩衝池不夠用時,將髒頁刷回磁盤
3重做日誌不可用時,刷新髒頁
重做日誌不可用的情況:因爲當前事務數據庫系統對重做日誌的設計都是循環使用的,並不能無限增大。重做日誌可以被重用的部分指這些日誌已經不再需要,這部分數據就可以被覆蓋使用,如果此時重做日誌還需要使用的話,就強制CheckPoint,將緩衝池中的頁至少刷新到當前重做日誌的位置。
兩種CheckPoint:Sharp CheckPoint 、 Fuzzy CheckPoint
Sharp CheckPoint:發生數據庫關閉時,將所有髒頁刷新回磁盤
Fuzzy CheckPoint:只刷新一部分髒頁,而不是所有。
4種可能導致Fuzzy CheckPoint的情況:
1Master Thread CheckPoint
2FLUSH_LRU_LIST CheckPoint
3Aysnc/Sync Flush CheckPoint
4Dirty Page to much CheckPoint
Master Thread
Master Thread具有最高的線程優先級別。內部由多個循環組成:主循環(loop),後臺循環(background loop),刷新循環(flush loop),暫停循環(suspend loop)
主循環(loop):主要進行兩大部分的操作:每秒鐘的操作和每10秒種的操作
每秒鐘的操作包括:
1日誌緩衝刷新到磁盤,即使該事務還未提交(總是) (解釋了爲什麼再大的事務的提交時間也是很短的)
2合併插入緩衝(可能 ,當前一秒內IO次數小於5次則執行)
3刷新至多100個innodb緩衝池的髒頁到磁盤(可能,判斷緩衝池中髒頁比例是否超過設置的閾值)
4如果當前沒有用戶活動,則切換到background loop(可能)
每10秒的操作包括:
1刷新100個髒頁到磁盤(可能,過去10秒鐘內IO次數小於200則執行)
2合併至多5個插入緩衝(總是)
3將日誌緩衝刷新到磁盤(總是)
4刪除無用的Undo操作(總是)
5刷新100個或10個髒頁到磁盤(總是)
後臺循環(background loop):如果當前沒有用戶活動或關閉數據庫,則會切換到background loop
執行的操作:
1刪除無用的Undo頁(總是)
2合併20個插入緩衝(總是)
3跳回到主循環(總是)
4不斷刷新100個頁直到符合條件(可能,跳轉到flush loop中完成)
若flush loop中無事可做,InnoDB引擎會切換到suspend_loop,將Master Thread掛起。
InnoDB引擎的關鍵特性
1插入緩衝(Insert Buffer)
含義:對於非聚集索引的插入或更新操作, 並不是每次直接插入到索引頁(由於非聚集索引的插入的離散型),而是判斷插入的非聚集索引頁是否在緩衝區,若是則直接插入,若不在則先放入InsertBuffer,再以一定頻率將Insert Buffer和輔助索引葉子階段進行merge操作,因爲此時通常能夠將多個插入合併到一個操作中,提高插入性能。
要滿足的兩個條件:
1索引是輔助索引
2索引不是唯一的
合併InsertBuffer時機:
1輔助索引頁被讀取到緩衝池
2Insert Buffer Bitmap檢測到該輔助索引頁已無可用空間
3Master Thread
2兩次寫(Double Write)
含義:引入目的爲提高數據庫的可靠性,在應用重做日誌前,用戶需要一個頁的副本,當寫入失效發生時,先通過該頁的副本還原該頁,再按重做日誌進行恢復,主要避免某個頁寫入一半發生了錯誤而導致頁損壞。
組成:分爲兩部分。一部分是內存中的doublewrite buffer,大小2MB,一部分是物理磁盤上的共享表空間中連續的128頁,即兩個區,也是2MB。
過程:對緩衝區的髒頁進行刷新時,並不直接寫磁盤,而是先通過memcpy函數將髒頁複製到doublewrite buffer,然後doublewrite buffer分兩次寫,每次1M寫入到共享表空間的物理磁盤,然後馬上調用fsync函數,同步磁盤,避免緩衝寫帶來的問題。
3自適應哈希索引(Adaptive Hash Index)
含義:InnoDB會監控對錶上各索引頁的查詢,如果構建哈希索引能夠帶來速度的提升,則會構建哈希索引(AHI)。AHI是根據緩衝池中的B+數索引頁來構建的,不必對整張表構建AHI,因此構建速度很快。InnoDB會根據訪問的模式和頻率自動爲某些熱點數據構建哈希索引。
要求:
1這個頁的連續訪問模式必須相同,如where a=XXX或where a=xxx and b=xxx 但是兩種模式不能交替執行。
2以該模式訪問了100次
3頁通過該模式訪問了N次,其中N=頁中記錄數/16;
適用範圍:只適合等值查詢,不適合範圍查詢等
4異步IO(Async IO)
優勢:
1用戶發起IO請求後可以立即執行其他操作無需等待。
2可以進行IO merge操作,將多個IO請求合併爲一個。
5刷新鄰接頁(Flush Neighbor Page)
含義:當刷新髒頁時,InnoDB存儲引擎會檢測該頁所在區的所有頁,如果是髒頁,則一起刷新。
好處:便於AIO進行IO請求合併(適合於機械硬盤)
第四章:表
索引組織表:InnoDB存儲引擎中,表都是根據主鍵順序組織存放的,這種組織方式爲索引組織表。
InnoDB中每張表都有主鍵,如果用戶沒有給定主鍵,會採用下列方案創建主鍵:
1首先判斷表中是否由非空的唯一索引(Unique NOT NULL),如果有將該字段設爲主鍵。(如有多個,則選擇建表時定義索引的第一個)
2如果不符合上述條件,則會自動創建一個6字節大小的指針。
InnoDB邏輯存儲結構:所有數據被邏輯的存放在一個空間裏,稱爲表空間(tablespace),表空間又由段(segment)區(extent)頁(page)組成。頁中還有一些文檔有時稱爲塊(block)
表空間:ibdata1,邏輯存儲結構的最高層,所有數據都放在表空間。
段:表空間由各個段組成,包括數據段(Leaf segement),索引段(Not-leaf segement),回滾段(Rollback segement)等
區:由連續的頁組成的空間,每個區大小固定爲1MB,默認情況下頁的大小爲16kB,所以默認一個區有64個連續的頁。
頁:磁盤管理的最小單位,默認爲16KB,頁大小一旦確定就無法修改。常見頁類型有:
數據頁(B-tree Node)
Undo頁(Undo log page)
系統頁(System Page)
事務數據頁(Transaction system page)
插入緩衝位圖頁(Insert Buffer Bitmap)
插入緩衝空閒列表頁(Insert Buffer Free List)
未壓縮的二進制大對象頁(Uncompressed BOLB Page)
壓縮的二進制大對象頁(Compressed BOLB Page)
行:InnoDB引擎是面向列的,也就是數據是按行存儲的。每個也做多存儲16KB/2-200行的記錄(即7992行)。
行記錄格式分爲:Compact和Redundant(爲兼容之前版本)兩種(屬於Antelope文件格式)
Compact格式:爲了高效的存儲數據(即在一個頁中放更多的數據)而引入,存儲格式:變長字段長度列表(按列逆序),NULL標誌位,記錄頭信息,列1數據,列2數據。。。每行數據有兩個隱藏列,事務ID列和回滾指針列,另外如果表沒有指定主鍵,則還有一個6字節的列rowid。Compact格式下NULL值不佔任何空間。
Redundant格式:存儲格式:變長字段偏移列表(按列逆序),記錄頭信息,列1數據,列2數據。。。
行數據溢出:InnoDB會將某些大的數據真正的數據以外(BLOB,LOB,VARCHAR等並不一定)評判標準:一個頁至少有兩條行記錄。
新的文件格式Barracuda:Compressed和Dynamic兩種行記錄格式。
新的兩種行記錄格式對於存放在BLOB中的數據採用了完全的行溢出方式。另外Compressed行記錄格式會對存儲其中的記錄採用zlib的算法進行行壓縮。
約束和索引的區別:約束是一個邏輯概念,用來保證數據的完整性。索引是一個數據結構,既有邏輯概念,又在數據庫中代表物理存儲的方式。
物化視圖:Oracle支持物化視圖,可以預先計算並保存多表的鏈接(Join)或聚集(Group By)等耗時的sql操作,這樣在進行復雜的查詢時可以避免這些耗時的操作。
Mysql支持水平分區,不支持垂直分區。此外Mysql的分區時局部分區索引,一個分區既包含了數據又包含了索引,全局分區是指,數據存放在各個分區,而數據的索引集中存放在一起,目前Mysql不支持全局分區。
常見的分區方式有:RANGE分區、LIST分區、Hash分區、KEY分區,Columns分區。
第五章、索引與算法
InnoDB支持以下幾種常見的索引:
1B+樹索引
B+樹索引只能找到被查找數據行的頁,再將該頁讀入內存,在頁中查找行。並不能直接找到對應行。
由於每頁PageDirectory中的糟是按照主鍵存放的,對於頁中的具體記錄是採用二分法查找的。
2全文索引
3哈希索引
聚簇索引(clustered index)和輔助索引(secondary index)的區別:聚簇索引葉子節點存放的是一整行的記錄,而輔助索引存放的是指向記錄的指針。因此聚簇索引能夠直接獲取行的信息,輔助索引則還需要一次指針定位。這也意味着更新操作對聚簇索引的影響更大。
聚簇索引的特點:
聚簇索引對於主鍵的範圍查詢和排序查詢非常快,適合like 'xxx%'(索引前綴查找)。
聚簇索引的存儲並不是物理上連續的,而是邏輯上連續的。主要體現在以下兩點:
1數據頁是通過雙向鏈表連接的,頁按主鍵順序連續。
2頁中記錄也是通過雙向鏈表維護的,物理上可以不按主鍵存儲。
輔助索引:不包含行信息。除了包含鍵值外,每個節點還包含一個書籤(bookmark),用來告訴InnoDB引擎哪裏可以找到與索引相對應的行數據(即相應數據行的聚簇索引鍵,再通過聚簇索引找到對應的行記錄)
Mysql5.5前創建或刪除索引的方式:
1創建一張新臨時表,表結構爲通過命令ALTER TABLE新定義的結構
2將原來表數據導入到新臨時表
3刪除原來表
4把臨時表命名爲原表
InnoDB 1.0x開始支持一種Fast Index Creation(FIC)的索引創建方式。在創建輔助索引時,對該表加一個S鎖。在創建過程中不需要重建表。刪除輔助索引則只需要Innodb更新內部視圖,並將輔助索引的空間標記爲可用,然後刪除mysql數據庫內部視圖對該表的索引定義即可。
添加B+樹索引的情況:
1.經常訪問表中很少一部分行記錄時
2.某個字段取值範圍很廣,幾乎沒有重複時
show index查詢中的Cardinality值就是索引中不重複記錄數量的預估值
3.滿足上述條件並且經常作爲查詢條件的字段
Cardinality值:
對Cardinality值的獲取是通過採樣方式完成(減少開銷)
對Cardinality值的更新是發生在INSERT和UPDATE操作中,更新策略:
1表中1/16數據已經發生了變化
2stat_modified_counter>2000000000(發生變化的次數)
對Cardinality值的更新是通過對8個葉子節點進行採樣,每次統計也會更新Cardinality值
聯合索引:本質上也是B+樹,不同的是聯合索引的鍵值數量不是1,而是大於等於2(有可能避免排序,如查詢一個人最近購買的N件商品)
覆蓋索引(covering index):即可以從輔助索引中直接得到查詢的記錄,而不需要再訪問一次聚集索引,但輔助索引不包含整行記錄的所有信息,可以減少大量的IO操作。
優化器不選擇使用索引的情況:
Multi-Range Read(MRR)優化:mysql5.6開始支持MRR優化。目的是減少磁盤的隨機訪問,轉化爲較爲順序的數據訪問。可以用來優化,range,ref,eq_ref類型的查詢。
MRR優化的好處:
1是數據訪問變得比較順序,查詢輔助索引時,首先根據查詢到的結果根據主鍵進行排序,然後按照主鍵排序的順序進行書籤查找。
2減少緩衝池中頁被替換的次數。
3批量處理對鍵值的查詢操作。
對於InnoDB和MyISAM存儲引擎的範圍查詢和JOIN操作,MRR的工作方式如下:
1將查詢到的輔助索引鍵值放到一個緩存中,此時緩衝中數據是根據輔助索引鍵值排序的。
2將緩存中的鍵值根據rowid進行排序。
3根據rowid的順序來訪問實際數據文件。
Index Condition Pushdown(ICP)優化:mysql5.6開始支持ICP優化。
在不支持ICP前,進行索引查詢時,先根據索引查找記錄再根據WHERE條件來過濾記錄。引入ICP後可以在取出索引記錄的同時進行WHERE條件過濾(將WHERE的部分過濾操作放在存儲引擎層),大大減少上層sql對記錄的索取(fetch)。
ICP支持range,ref,eq_ref,ref_or_null類型的操作。
InnoDB中的哈希算法:衝突機制採用鏈表法,哈希函數採用除法散列方式
第六章、鎖
lock和latch:
latch是一種輕量級鎖,其要求鎖定的時間必須非常短。又可以分爲mutex(互斥鎖)和rwlock(讀寫鎖),其目的是用來保證併發進程操作臨界資源的正確性,通常沒有死鎖檢測的機制。
lock的對象是事務,用來鎖定數據庫中的對象,如表、頁、行。一般lock的對象僅在事務commit或rollback後釋放,lock有死鎖機制。
鎖(lock)的類型:
InnoDB提供了兩種標準的行級鎖:
共享鎖(S Lock):允許事務讀一行數據
排他鎖(X Lock):允許事務刪除或更改一行數據
InnoDB支持意向鎖,意向鎖是將鎖的對象分爲多個層次,如果把上鎖的對象看成一個棵樹,那麼對最下層的對象上鎖,也就是對最細粒度的對象上鎖,那麼首先要對粗粒度的對象上鎖。例如,如果要對頁上的記錄r加X鎖,那麼分別需要對數據庫、表、頁上加意向鎖IX鎖,再在記錄r上加X鎖。
InnoDB的意向鎖爲表級別的鎖,其目的是爲了在一個事務中揭示下一行被請求鎖的類型,其支持兩種意向鎖:
意向共享鎖(IS Lock):事務想要獲取一張表某幾行的共享鎖
意向排他鎖(IX Lock):事務想要獲取一張表某幾行的排他鎖
InnoDB存儲引擎支持的鎖 行級別的鎖,因此意向鎖其實不會阻塞除全表掃描以外的其他操作。
一致性非鎖定讀(REPEATABLE READ級別):InnoDB通過行多版本控制的方式來讀取當前執行時間數據庫中的行數據。如果當前讀取的行正在執行DELETE或UPDATE操作,這時候讀取操作不會因此去等待行上鎖的釋放,而是會讀取一個行的快照數據(數據未修改時的版本)。其實現是通過undo段來完成。
具體實現:多版本併發控制(MVCC) 參考: https://www.cnblogs.com/lmj612/p/10598971.html
一致性鎖定讀:InnoDB對SELECT語句支持兩種一致性鎖定讀操作:
SELECT ... FOR UPDATE (加X鎖)
SELECT ... LOCK IN SHARE MODE (加S鎖)
自增長與鎖:
解釋:InnoDB的內存中每個含有自增長值的表都含有一個自增長計數器(auto-increment counter),每次插入,這個計數器值就+1,這種實現方式叫做AUTO-INC Locking,是一種特殊的表鎖機制,此鎖並不是事務完成後釋放,而是自增長值插入的SQL語句結束時釋放。
性能問題:
1對自增長值的列併發插入性能較差,事務必須等待前一個插入的完成。
2對於INSERT...SELECT的大數據量的插入會影響插入的性能,因爲另一個事務的插入會被阻塞。
Mysql版本5.1.22後,InnoDB提供了一種輕量級互斥量的自增長實現機制。
外鍵和鎖:
解釋:InnoDB中如果對於一個外鍵列沒有顯示添加索引,系統會自動給此列添加一個索引,這樣看可以避免表鎖(爲啥?)
注意:對於外鍵值的插入或更新,首席按要查詢父表中的記錄,即SELECT父表。但是對父表的SELECT操作並不是使用一致性非鎖定讀的方式,因爲這樣會導致數據不一致,而是採用SELECT ... LOCK IN SHARE MODE的方式,即主動對父表加一個S鎖。如果此時父表已經加了X鎖,則子表的操作會被阻塞。
鎖的算法:
行鎖的算法:InnoDB引擎上有三種行鎖算法:
Record Lock:單個行記錄上的鎖
Gap Lock:間隙鎖,鎖定一個範圍,但不包含記錄本身
Next-Key Lock:Gap Lock + Recent Lock,鎖定一個範圍,並鎖定記錄本身。
InnoDB所有的行鎖算法都是基於索引實現的,鎖定的也都是索引或索引區間;
InnoDB對行的查詢都採用Next-Key Lock,如果一個索引有10,11,13,20這四個值,那麼該索引可能被Next-Key Lock的區間爲(-無窮,10](10,11],(11,13],(13,20],(20,+無窮),其設計的目的是爲了解決Phantom Problem。如果查詢的索引含有唯一屬性,則將其降級爲Recent Lock(查詢的列有唯一索引纔會降級,若是輔助索引則不會,輔助索引除了對加Next-Key Lock外還會對輔助索引的下一個鍵值加上Gap Lock)
兩種方式顯式關閉Gap Lock:
1將事務隔離級別設置爲READ COMMITTED
2將參數innodb_locks_unsafe_for_binlog設置爲1
可能導致的問題:破壞事務的隔離性,對於replication,可能導致主從數據不一致。
解決Plantom Problem(幻象問題)
Plantom Problem含義:指同一事務下連續執行兩次相同的SQL操作可能導致不同的結果,第二次SQL語句可能返回之前不存在的行。
髒讀:一個事務可以讀到另一個事務未提交的信息,違反了事務的隔離性。例如:事務A對插入了一條數據,還未提交,事務B讀取時讀到了插入的數據,之後事務A進行了回滾,這樣B就讀到了不存在的數據。
不可重複讀:一個事務多次讀取一個數據集合,由於其間其他事務對數據進行了修改,導致兩次讀取的結果不同。違反了事務的一致性要求。
髒讀和不可重複讀的區別:髒讀讀到的是未提交的數據,不可重複讀讀到的是已提交的數據。
丟失更新:兩個事務同時對一個數據進行更新操作,導致其中一個事務的更新操作被覆蓋。(即使是最低級別的READ UNCOMMITED也能避免這種情況)
死鎖的處理:
超時
wait-for graph(等待圖):是一種更主動的死鎖檢測方式。InnoDB採用這種方式。圖中主要存在兩種信息:
鎖的信息鏈表
事務等待鏈表
鎖升級:將當前鎖的粒度降低。例如將1000個行鎖升級爲一個頁鎖,或者將頁鎖升級爲表鎖。
InnoDB不存在鎖的升級,因爲其不是根據誒個記錄來生成行鎖的,其根據每個事務訪問的每個頁對鎖進行的管理,採用的是位圖的方式,鎖住多少記錄開銷影響不大。
第七章、事務
事物的ACID特性:
A 原子性(atomicity):事務被看作數據庫的邏輯工作單元。事務中的操作要麼全都成功,要麼全都失敗。
C 一致性(consistency):事務將數據庫從一種一致性狀態變爲另一種一致性狀態
I 隔離性(isolation):一個事務的執行不能被其他事務干擾。(任何事物的更新操作直到其提交成功,對其他事務都是不可見的)
D 永久性(durability):一個事務它對數據庫應該是永久的,即使出現故障其對數據庫的更新也是永久的。
事務的分類:
扁平事務
帶保存點的扁平事務
鏈事務
嵌套事務
分佈式事務
事務的實現
事務的隔離性由鎖機制保證,原子性,一致性,永久性由數據庫的redo log和undo log來完成。redo log保證原子性和永久性,undo log來保證一致性。
redo和undo可以視爲一種數據庫恢復操作,redo恢復提交事務的頁修改操作,而undo回滾行記錄到某個特定的版本,兩者的記錄內容不同,redo通常是物理日誌,記錄的是頁的物理修改操作,undo是邏輯日誌,根據每行記錄進行記錄。
redo:分爲兩部分:重做日誌緩衝,重做日誌文件。
InnoDB通過Force Log at Commit機制保證事務的持久性。即事務提交前,必須先將該事務的日誌寫入重做日誌文件中進行持久化。
log block:InnoDB存儲引擎中,重做日誌都是以512字節存儲的,這意味着重做日誌緩衝和重做日誌文件都是以塊(block)爲單位的。稱之爲重做日誌塊(redo log block),由於重做日誌塊大小和磁盤扇區大小一樣都是512字節,因此其寫入可以保證原子性,無需doublewrite。
重做日誌和二進制日誌比較:
共同點:兩者都記錄了對數據庫操作的日誌。
區別:
重做日誌在InnoDB存儲引擎層產生,二進制日誌是mysql上層產生,任何存儲引擎產生的數據庫操作都會記錄在二進制日誌中。
二進制日誌是一種邏輯日誌,記錄的是對應的sql語句,重做日誌是物理日誌,記錄的是對應頁的修改。
二進制日誌只在事務提交時寫入日誌,重組日誌在事務進行時不斷提交。
undo:主要用於回滾操作和MVCC。與redo存放在重做日誌文件中不同,undo存放在表空間的undo segment。
undo是邏輯日誌,只是將事務的修改邏輯地取消了,但數據結構和頁本身可能大不相同。例如用戶執行Insert 10W條記錄的語句時,可能系統會爲其申請一個大的段,表空間會增大。但是執行rollback操作時,申請的存儲空間並不會回收。實際上對於Insert操作,數據庫回滾是是執行相應的delete操作,對於update操作,會執行一個相反的update操作。
undo log會產生redo log,因爲undo log也需要持久性的保護。
purge操作:
舉例:
delete和update操作可能並不會立即執行,例如sql語句:delete from t where a=1。表t上有列a的聚集索引,有列b的輔助索引,上述delete操縱僅僅是將主鍵列的delete flag=1,記錄並沒有刪除還是存在於B+樹中,其次對於輔助索引上的a=1,b=1的記錄同樣沒有操作,甚至沒有產生undo log。其真正的刪除操作被延遲到purge操作中完成。
這樣的設計是因爲:innoDB支持MVCC,所以事務不能在記錄提交時立即處理。
附:
mysql爲什麼使用B+樹而不使用B樹
B樹每個節點都代表一個記錄,都存有數據域,這使得B樹的每個非葉子節點比B+要大的多,這也使得每次索引的I/O次數增加。
另外相比於B樹,B+樹有將所有葉子節點串聯起來的指針,可以順序訪問,很方便全表遍歷等操作,另外對於區間訪問也很方便。