mysql覆蓋索引之看山還是山

Wayne_Kdl發表於2019-12-09

前置知識 ReadView/MVCC 請看另一篇部落格:juejin.im/post/5ded11…

覆蓋索引定義

如果一個索引包含(或覆蓋)所有需要查詢的欄位的值,稱為‘覆蓋索引’。

看山是山

覆蓋索引,快不快,當然快,只用查詢二級索引就得到了所需資料,少了一步回表操作,當然快。

看山不是山

這幾天學了mvcc的知識,知道了對記錄更改需要生成undo日誌,構建版本鏈之類的,那麼問題來了,版本鏈是在聚簇索引,怎麼在二級索引檢視/更改版本鏈呢?那不就是要回表?

矛盾:

1:覆蓋索引不回表,那麼覆蓋索引查詢無法維護版本鏈
2:覆蓋索引回表,那麼覆蓋索引還快嗎?就不快了呀,但是部落格、面試,大家不都是異口同聲的說覆蓋索引快?

我的解決思路

既要覆蓋索引快,又要能通過二級索引維護版本鏈,那麼可能就是查詢的過程可能可以細分一下,具體有兩個思路:
1:二級索引查詢到資料,返回資料給客戶端,然後去聚簇索引維護版本鏈
2:維護版本鏈是個非同步的過程(其實想想就知道很不靠譜)

看山還是山

諮詢大佬之後學到了mysql的做法,在二級索引的頁中,維護這一個記錄了增刪改操作的事務的ID,SELECT生成ReadView時,判斷最小活躍事務ID是否大於頁面的增刪改最大事務ID,如果大於,說明頁面沒有更改過,不用回表,否則就得回表。
回表的時候,就跟之前分析過的mvcc流程一致了,不贅述了。

對比一下聚簇索引中的ReadView判斷流程和二級索引中的ReadView判斷流程

聚簇索引:

1:如果被訪問記錄的trx_id與creator_trx_id相同,即當前事務在訪問它自己修改過的記錄,可見。
2:記錄的trx_id小於min_trx_id值,表示生成該版本的事務已經提交,可見。
3:記錄的trx_id大於max_trx_id值,代表生成該版本的事務在當前事務之後才開啟,不可見。
4:min_trx_id < trx_id < max_trx_id,判斷trx_id是否在m_ids中,如果在,說明建立ReadView時(注意這裡是ReadView,而不是trx_id)該版本的事務還是活躍的,該版本不可以被訪問;如果不在,說明建立ReadView時生成該版本的事務已經提交,該版本可以被訪問。

二級索引:

在二級索引的頁中,維護這一個記錄了增刪改操作最大事務的ID,SELECT生成ReadView時,判斷最小活躍事務ID是否大於頁面的增刪改最大事務ID,如果大於,說明頁面沒有更改過,可見,否則不可見。

區別

聚簇索引中,如果大於min_trx_id,則還要判斷是否在m_ids中,而二級索引不判斷了,回表判斷
為什麼?
因為二級索引的頁中只儲存了一個值,假如它
1:> min_trx_id,在m_ids中,說明該事務還是活躍的,不可訪問。要回表去聚簇索引拿版本鏈。 2:> min_trx_id,不在m_ids中,說明該事務已經提交。那麼可以說該版本可以被訪問嗎? 按我的推算是可以的,雖然mysql沒有這麼做。

總結

所以,相對我的兩個寫死流程的解決辦法,mysql其實是做了一種平均值上縮減訪問時間的做法。 從mysql的很多設計看出,mysql很多地方都用了優化,往往不是非同步,因為非同步會帶來更多問題,是同步,但是對能優化的部分的流程進行了優化,不能優化的部分就不優化。
所以,覆蓋索引快嗎?確實快,但是不是查每一行記錄都快,而是平均值意義上的快,具體來看,有些行記錄查詢變快了,有些沒有,但是整體上來說,就是變快了。
面試的時候,問覆蓋索引為什麼快?因為不用回表,這個答案沒毛病,節省的不就是那部分不用回表的記錄的回表時間嗎?沒毛病。

PS:至於二級索引頁的“記錄了增刪改操作的事務的ID”,二級索引頁和buffer pool的矛盾,我已經不想再深究了。。。。。。

相關文章