mysql5.7決定SQL中IN條件是否走索引的成本計算,mysql中的index dive是在兩個區間之間計算有多少條記錄的方式


一、表和索引設計

CREATE TABLE `t_user`  (
  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT 'primary key',
  `username` varchar(40) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL COMMENT 'user name',
  `age` int(4) NOT NULL DEFAULT 20 COMMENT 'user age',
  `birthday_date_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT 'user birthday',
  `address` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NULL DEFAULT NULL,
  `remark` varchar(30) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL COMMENT 'remark something',
  `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT 'create time',
  `version` int(4) NOT NULL DEFAULT 0 COMMENT 'update version',
  PRIMARY KEY (`id`) USING BTREE,
  UNIQUE INDEX `idx_name`(`username`) USING BTREE,
  INDEX `idx_age_remark`(`age`, `remark`) USING BTREE,
  INDEX `idx_create_time`(`create_time`) USING BTREE,
  INDEX `idx_address`(`address`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 10003 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_bin ROW_FORMAT = DYNAMIC;

二、mysql預估表行數,和索引不重復行數

2.1)mysql預估t_user表行數 9822行:

2.2)mysql預估t_user表各個索引不重復數據行數:show index from t_user;

Non_unique : 是否為唯一索引
Key_name : 索引名稱
Seq_in_index : 索引列順序,如果是聯合索引,就會有數字排序123這樣子,其他單個列索引都是1
Cardinality : 索引基數。估算的該索引列的不重復值數據行數,所以如果該值和表行數數據一樣,說明該索引列沒有重復值。
Index_type : 索引類型,一般是BTREE索引

三、執行成本計算

【index dive】

在兩個區間之間計算有多少條記錄的方式,在mysql中被稱為index dive。如果一個SQL 用了 IN (2萬個參數,或者一個子查詢SQL結果集非常多的),那么mysql很有可能認為走全表掃描更快。
查看mysql的index dive參數值:

SHOW VARIABLES LIKE '%dive%';  -- 默認 eq_range_index_dive_limit = 200。在5.7.3版本以前,mysql這個值的設置是10


也就是當IN條件里的參數個數不超過200時,mysql才走index dive,去精確統計有多少行數,超過200個了,myslq會使用索引統計數據進行估算。那怎么看index dive會走索引還是全表掃描呢?看以下這個SQL例子。

【SQL例子】

EXPLAIN SELECT * FROM t_user WHERE username in ('10569d48-a94d-4ee4-831d-261b3777bd1f', 'e33a68d2-78af-497e-87e0-24516e73952fs');

執行成本 = address的索引列不重復行數預估數 1000 / 表預估總行數 9822 * 假設in條件里是2萬個 20000 = 2036.24 

參考index dive的默認設置是200,這里計算出的執行成本2036.24就太大了,mysql決定走全表掃描。

四、mysql對in條件查詢做了優化

1)物化臨時表方法。select * from t1 where m1 in ( select m2 from t2 where m2 = 'a' );   如果結果集過大,mysql會根據temp_table_size參數去做物化,即建立臨時表,至於是內存臨時表,還是磁盤臨時表,mysql要看數據大小。 加入建立的臨時表叫t_temp,那mysql會將此sql優化成二表連查: select t1.* from t1, t_temp on t1.id = t_temp.id;

2)Semi-join半連接。對於in條件查詢,mysql的SQL優化器會做3種優化:

    2.1 表上拉。比如子查詢里面的=條件就是唯一索引,只會找到一條記錄,那么就是等價於inner join

    2.2 重復值消除。 mysql會建立一個臨時表去重

    2.3 松散掃描。在子查詢中,如果使用了一個索引列去查詢,只需要掃描二級索引但是只取值相同的第一條到子查詢外部去匹配的方式,就叫松散掃描。

3)所有的in條件查詢都可以轉化為exists查詢,這樣還有可能走索引。

end.


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM