Innodb:使用索引避免排序後DESC和ASC的區別

一、問題由來

這個問題是朋友提出的,大概意思就是說,Innodb 的記錄中只會包含rec next的位置,那麼塊內部反向掃描如何完成的,比如使用索引避免排序後的DESC操作。
實際上這個問題可以簡單描述爲Innodb 塊內部是單向鏈表,如果要反向掃描那麼是如何完成的。

二、相關接口

實際上對於正向和反向掃描記錄使用的方法並不一致,大概的接口爲:

  • 正向:page_rec_get_next_low
    這裏很容易看出就是通過rec next位置來確認下一條記錄,這非常容易也非常簡單,代價極小。

  • 反向:page_rec_get_prev_const
    這個函數實際上可以簡單的看一下就知道獲取prev(上一條記錄)就複雜了一些,它通過slot來進行定位,然後進行循環比對來獲取上一條記錄。

三、反向(DESC)大概獲取流程

首先我們要清楚slot是幹什麼的,實際上在定位數據的時候會先定位到slot,然後再在slot內部做一個二分法。slot對於記錄來講是有序的,即邏輯上是順序的(非物理順序)。一個slot爲2字節存儲的是對應記錄的偏移量,而一個slot最多包含8條記錄,關於slot的講解很多書籍都有說明,不再熬述。

好了我們假設上一次讀取到的prev row = G,而slot內部包含了A、B、C、D、E、F、G 7條記錄。

  • 第一步定位到slot的開頭即記錄A的位置
    LOOP:
  • 設置本次 prev row = A
  • 通過A的rec next位置獲取下一條記錄 B
  • 是否 B == G
    {如果是則記錄prev row= A 停止循環}
    否則
    {繼續循環,下一次循環prev row = B }

這個循環一直要持續到 prev row = F ,即 G == G 成立才結束。循環次數爲6次。

可以看到這樣上一條記錄就找到了,不過看起來代價比ASC方式大了很多很多。

四、如何避免

MySQL 8的降序索引值得擁有。或者規避這個問題。

五、debug棧幀和結果

  • 1、DESC 多次循環
983             if (page_is_comp(page)) {
(gdb) n
984                     while (rec != rec2) {
(gdb) n
985                             prev_rec = rec2;
(gdb) n
986                             rec2 = page_rec_get_next_low(rec2, TRUE);
(gdb) n
984                     while (rec != rec2) {
(gdb) n
985                             prev_rec = rec2;
(gdb) n
986                             rec2 = page_rec_get_next_low(rec2, TRUE);
(gdb) n
984                     while (rec != rec2) {
(gdb) n
985                             prev_rec = rec2;
(gdb) n
986                             rec2 = page_rec_get_next_low(rec2, TRUE);
(gdb) n
984                     while (rec != rec2) {
(gdb) n
985                             prev_rec = rec2;
(gdb) n
986                             rec2 = page_rec_get_next_low(rec2, TRUE);
....
  • 2、調用棧幀
#0  page_rec_get_prev_const (rec=0x7fffcf72811e "\200") at /root/mysqlall/percona-server-locks-detail-5.7.22/storage/innobase/include/page0page.ic:966
#1  0x0000000001c607a1 in page_rec_get_prev (rec=0x7fffcf72811e "\200") at /root/mysqlall/percona-server-locks-detail-5.7.22/storage/innobase/include/page0page.ic:1010
#2  0x0000000001c60bc0 in page_cur_move_to_prev (cur=0x7ffe440c53a8) at /root/mysqlall/percona-server-locks-detail-5.7.22/storage/innobase/include/page0cur.ic:186
#3  0x0000000001c614a9 in btr_pcur_move_to_prev_on_page (cursor=0x7ffe440c53a0)
    at /root/mysqlall/percona-server-locks-detail-5.7.22/storage/innobase/include/btr0pcur.ic:277
#4  0x0000000001c6310d in btr_pcur_move_to_prev (cursor=0x7ffe440c53a0, mtr=0x7ffef07f9660)
    at /root/mysqlall/percona-server-locks-detail-5.7.22/storage/innobase/btr/btr0pcur.cc:630
#5  0x0000000001b7c225 in row_search_mvcc (buf=0x7ffe440c6ab0 "\375\v", mode=PAGE_CUR_L, prebuilt=0x7ffe440c5180, match_mode=0, direction=2)
    at /root/mysqlall/percona-server-locks-detail-5.7.22/storage/innobase/row/row0sel.cc:6284

 

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