MYSQL Skip Scan Range 小功能,解決大問題 ?
- 2019 年 12 月 12 日
- 筆記
用過MYSQL的都會被別的資料庫的operation 吐槽,索引的建立與使用方面的需要掌握的知識是比較「矯情的」。為什麼這麼說,在MYSQL 5.X中如果一個表中 有這樣的索引,和這樣的查詢,索引的效率就會大打折扣。
我們來看一下,根據官方的文檔我們創建下面的數據
請準備MYSQL 8.013以上版本 以及 MYSQL 5.7 版本的兩台機器,並執行下面的操作
CREATE TABLE t1 (f1 INT NOT NULL, f2 INT NOT NULL, PRIMARY KEY(f1, f2)); INSERT INTO t1 VALUES (1,1), (1,2), (1,3), (1,4), (1,5), (2,1), (2,2), (2,3), (2,4), (2,5); INSERT INTO t1 SELECT f1, f2 + 5 FROM t1; INSERT INTO t1 SELECT f1, f2 + 10 FROM t1; INSERT INTO t1 SELECT f1, f2 + 20 FROM t1; INSERT INTO t1 SELECT f1, f2 + 40 FROM t1; ANALYZE TABLE t1;
EXPLAIN SELECT f1, f2 FROM t1 WHERE f2 > 40;
set session optimizer_trace=』enabled=on';
然後我們通過optimizer_trace 來查看這兩個伺服器上查詢上面的給出的分析結果
下面的圖形僅僅只給出不同的截圖(具體 optimize_trace 之前有文字寫過,這裡就不贅述了)
1 MYSQL 5.7 的 row_estimation ,我看可以看到很簡單

2 下面是MYSQL 8.017 的圖,從下圖看,明顯的MYSQL 8 在查詢計劃的分析要比 MYSQL 5.7 複雜的多,其中第二張圖已經顯示走了skip_scan


既然看到不同,但問題是這樣有什麼用,首先如果是MYSQL 5.7 上基本上走的是 INDEX Scan 而, 而在MYSQL 8 上做的事情要遠遠多於 MYSQL5.7 從上圖可以看出,首先查詢先將索引中的前邊的欄位,進行了group by 的操作,將需要進行掃描的數據通過第一個欄位劃分了塊,然後在每個塊中掃描range 的數據。
這樣的好處也是顯而易見的,如果將數據掃描進行分塊處理,有些不包含range 的數據塊將不被掃描,或者不包含range 的行也將不被掃描。
這項功能也是有一定要求的
1 必須單表
2 不能有group distinct 的操作
3 索引兩邊的欄位都可以包含NULL ,但中間的欄位不可以有NULL
下面在做一個測試確認一下前邊有兩個欄位的情況下,是不是也是可以走skip scan index
CREATE TABLE t2 (f1 INT NOT NULL, f2 INT NOT NULL,f3 int not null);
INSERT INTO t2 VALUES
(1,1,2), (1,2,3), (1,3,4), (1,4,5), (1,5,6),
(2,1,3), (2,2,4), (2,3,6), (2,4,2), (2,5,4);
INSERT INTO t2 SELECT f1, f2 + 5,f3 + 2 FROM t2;
INSERT INTO t2 SELECT f1, f2 + 10,f3 + 4 FROM t2;
INSERT INTO t2 SELECT f1, f2 + 20, f3 + 5 FROM t2;
INSERT INTO t2 SELECT f1, f2 + 4, f3 + 6 FROM t2;
ANALYZE TABLE t1;
create index ix_t2_f2_f3 on t2 (f1,f2,f3);

最終的結果還是可以走的,其實可以理解為,前邊將兩個沒有條件的欄位都變為有具體值的條件匹配後面的欄位的範圍查詢。這個做法在ORACLE 早就是有的功能,目前MYSQL 也繼承了這個功能。
