MySQL:一個特殊的問題

gaopengtttt發表於2020-06-27

---

這是開發問的一個問題


---

###一、問題描述


如果一個表有一個聯合索引 (a,b):


索引記錄如下:

id1 id2

 1   10

 1   10

 1   10

 2   20

 2   20

 3   30

 3   30

 

 意思就是說透過a進行過濾的資料和透過a和b過濾的資料條數一樣,如果要透過索引訪問資料select * from table where id1=1 和 select * from table where id1=1 and id2=10 是不是效率一致?


###二、簡易分析


首先對於資料查詢,首先第一步是進行innodb層資料的定位(也就是從什麼位置開始輸出資料給MySQL層),然後定位完成後,順序向下訪問就行了,因為innodb 的表示一個索引組織表。

- 對於等值條件,Innodb層會多訪問下一條資料,如果不符合要求了則表示結束了,會返回一個DB_RECORD_NOT_FOUND標記給MySQL層表示結束。

- 對於 範圍 比如 >  and < 操作如果使用了索引,那麼<操作要麼在MySQL層進行判斷結束,要買使用ICP在Innodb做一層過濾。


對於資料定位後的順序訪問而言,沒有什麼差別。


因此這個問題主要的問題轉換為, a=* 和 a=* and b=* 在進行資料定位的時候效率是否一致,對於這個問題來講因為a=* 和 a=* and b=* 返回的資料是一樣。所謂資料定位找到資料返回的起點在什麼地方,這個過程一般會透過B+數定位到葉子節點,然後根據在葉子節點內部使用二分法進行比較,加速定位,比較的方法就是根據欄位個數逐個比較(函式cmp_dtuple_rec_with_match_bytes),比如a=* 那麼需要比較就是一個,如果a=* and b=* 那麼比較的欄位就是2個。


程式碼大概的棧

```

->row_search_mvcc

  ->btr_pcur_open_with_no_init_func

    -> btr_cur_search_to_nth_level

      -> page_cur_search_with_match_bytes

        -> cmp_dtuple_rec_with_match_bytes

```

cmp_dtuple_rec_with_match_bytes 這部分註釋如下:


```

/* Match fields in a loop; stop if we run out of fields in dtuple

or find an externally stored field */


while (cur_field < n_cmp) {

```

n_cmp就是需要比較的個數。


debug 記錄:

```

a=*記錄

829             ut_ad(n_cmp <= dtuple_get_n_fields(dtuple));

(gdb) n

830             ut_ad(cur_field <= n_cmp);

(gdb) p n_cmp

$4 = 1


a=* and b=* 記錄


829             ut_ad(n_cmp <= dtuple_get_n_fields(dtuple));

(gdb) n

830             ut_ad(cur_field <= n_cmp);

(gdb) p n_cmp

$5 = 2

```


因此結論:如果這種特殊情況下,我認為效率應該基本一致,僅僅區別就在於定位比較的時候使用欄位的多少,但這不會是語句的最大效能耗用點,最大的應該是迴圈訪問所有的欄位和透過主鍵回表。因此儘量寫自己需要的欄位。




深入理解MySQL主從原理:

個人微信:gaopp_22389860


來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/7728585/viewspace-2700762/,如需轉載,請註明出處,否則將追究法律責任。

相關文章