必須知道的SQL語句不走索引時的排查利器

前言:

在索引優化時,常常會看到的一句話:若是索引字段出現隱式字符集轉換的話,那麼索引將失效,進而轉爲全表掃描,查詢效率將大大下降,要避免出現隱式字符集轉換;html

在此我想問問同窗們:

  • 你們知道爲何隱式字符集轉換會致使索引失效嗎?mysql

  • 實際場景中有沒有遇到過隱式字符集轉換致使索引失效的場景,具體排查的過程;sql

本文主線:

由上面的兩個問題牽引出了本文的主線;微信

  • 簡單描述下隱式字符集轉換致使索引失效的緣由網絡

  • 而後模擬實際場景排查隱式字符集轉換致使索引失效的過程數據結構

隱式字符集轉換致使索引失效的緣由

MySQL索引的數據結構是 B+Tree,想要走索引查詢必需要知足其 最左前綴原則 ,不然沒法經過索引樹進行查找,只能進行全表掃描;函數

例如:下面的這個SQL因爲在 索引字段 上使用函數進行運算,致使索引失效工具

select * from t_user where SUBSTR(name, 1, 2) = '李彤'

上面的這個SQL怎麼改造才能使索引生效呢?以下所示:性能

select * from t_user where name like '李彤%'

經過上面的小例子能夠知道,若是在索引字段上使用函數運算,則會致使索引失效,而索引字段的 隱式字符集轉換 因爲MySQL會自動的在索引字段上加上 轉換函數 ,進而會致使索引失效;學習

那接下來咱們就經過模擬的實際場景來具體看看是否是因爲MySQL自動給加上了轉換函數而致使索引失效的;

模擬場景 + 問題排查

因爲致使索引失效的緣由有不少,若是本身寫的SQL怎麼看都沒問題,可是經過查看執行計劃發現就是沒有走索引查詢,此時就會讓不少人陷入困境,這究竟是怎麼致使的呢?

此時本文重點將要講述的工具就要閃亮登場啦: explain extended + show warnings

使用這個工具能夠將執行的SQL語句的一些擴展信息展現出來,這些擴展信息就包括:MySQL優化時可能會添加上字符集轉換函數,使得字符集不匹配的SQL能夠正確執行下去;

下面就來具體聊聊 explain extended + show warnings 的使用;

模擬隱式字符集轉換的場景:

首先建立兩個字符集不同的表:

CREATE TABLE `t_department` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `de_no` varchar(32) NOT NULL,
  `info` varchar(200) DEFAULT NULL,
  `de_name` varchar(200) DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `index_de_no` (`de_no`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=12 DEFAULT CHARSET=utf8mb4;


CREATE TABLE `t_employees` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `em_no` varchar(32) NOT NULL,
  `de_no` varchar(32) NOT NULL,
  `age` int(11) DEFAULT NULL,
  `info` varchar(200) DEFAULT NULL,
  `em_name` varchar(200) DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `index_em_no` (`de_no`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=12 DEFAULT CHARSET=utf8;

而後使用存儲過程構造數據:

# 若是存儲過程存在則刪除 
DROP PROCEDURE IF EXISTS proc_initData;

DELIMITER $
# 建立存儲過程
CREATE PROCEDURE proc_initData()
BEGIN
    DECLARE i INT  DEFAULT 1;
    WHILE i<=30 DO
        # 新增數據
        INSERT INTO t_employees ( em_no, de_no, info, em_name , age) VALUES ( CONCAT('001', i), '003', 'test11', 'test2', i ); #執行的sql語句
        SET i = i+1;
    END WHILE;
END $

# 調用存儲過程
CALL proc_initData();

注意:在構造數據時,記得將 t_employees 表中的 de_no 字段值構造的 離散些 ,由於若是索引字段值的 區分度很低 的話,那麼MyQSL優化器經過採樣統計分析時,發現索引查詢和全表掃描性能差很少,就會直接進行全表掃描了;

索引失效的查詢SQL語句:

將表和數據構造完後,咱們使用SQL語句進行查詢下,而後再看看其執行計劃;

explain 
select * from t_department a LEFT JOIN  t_employees b on a.de_no = b.de_no where a.id = 16

其執行計劃以下:

發現 t_employees 表中的 de_no 字段有索引,可是沒有走索引查詢,type=ALL 走的全表掃描,可是經過查看SQL語句發現其沒有問題呀,表面看上去都是知足走索引查詢的條件呀,排查到這發現遇到了困境,苦惱啊!

還好,經過在網絡世界上遨遊,最終發現了 explain extended + show warnings 利器,利用它快速發現了索引失效的根本緣由,而後快速找到了解決方案;

下面就來聊聊這個利器的具體使用,開森!

使用利器快速排查問題:

注意:explain 後面跟的關鍵字 EXTENDED(擴展信息) 在MySQL5.7及以後的版本中廢棄了,可是該語法仍被識別爲向後兼容,因此在5.7版本及後續版本中,能夠不用在 explain 後面添加 EXTENDED 了;

EXTENDED關鍵字的具體查閱資料:https://dev.mysql.com/doc/refman/5.7/en/explain-extended.html

具體使用方法以下:

①、首先在MySQL的可視化工具中打開一個 命令列介面 :工具 --> 命令列介面

②、而後輸入下面的SQL並按回車:

explain EXTENDED
select * from t_department a LEFT JOIN  t_employees b on a.de_no = b.de_no where a.id = 4019;

③、而後緊接着輸入命令 show warnings; 並回車,會出現以下圖所示內容:

經過展現出的執行SQL擴展信息,發現MySQL在字符集不一致時自動添加上字符集轉換函數,由於是在 索引字段 de_no 上添加的轉換函數,因此就致使了索引失效;

而若是咱們沒看擴展信息的話,那麼可能直到咱們查看錶結構的時候纔會發現是因爲字符集不一致致使的,這樣就會花費不少的時間;

擴展:隱式類型轉換

我們聊完上面的隱式字符集轉換致使索引失效的狀況,再來簡單聊聊另外一種 隱式類型轉換 致使索引失效的狀況;

隱式類型轉換:簡單的說就是字段的類型與其賦值的類型不一致時會進行隱式的轉換;

小例以下:

select * from t_employees where em_name = 123;

上面的SQL中 em_name 爲索引字段,字段類型是 varchar,爲其賦 int 類型的值時,會發現索引失效,這裏也能夠經過 explain extended + show warnings 查看,會發現以下圖所示內容:

至此本文進入結尾,在此再說明下,上文中測試時使用的MySQL版本都是 5.7

♡ 點贊 + 評論 + 轉發 喲

若是本文對您有幫助的話,請揮動下您愛發財的小手點下贊呀,您的支持就是我不斷創做的動力,謝謝啦!

您能夠微信搜索 【木子雷】 公衆號,大量Java學習乾貨文章,您能夠來瞧一瞧喲!

相關文章
相關標籤/搜索