亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 數據庫 > MySQL > 正文

一個優化MySQL查詢操作的具體案例分析

2024-07-24 13:07:04
字體:
來源:轉載
供稿:網友

這篇文章主要介紹了一個優化MySQL查詢操作的具體案例分析,主要針對join字段的使用方面做出調整,需要的朋友可以參考下

問題描述

一個用戶反映先線一個SQL語句執行時間慢得無法接受。SQL語句看上去很簡單(本文描述中修改了表名和字段名):

SELECT count(*) FROM a JOIN b ON a.`S` = b.`S` WHERE a.`L` > '2014-03-30 00:55:00' AND a.`L` < '2014-03-30 01:00:00' ;

且查詢需要的字段都建了索引,表結構如下:

 

 
  1. CREATE TABLE `a` ( 
  2. `L` timestamp NOT NULL DEFAULT '2000-01-01 00:00:00'
  3. `I` varchar(32) CHARACTER SET utf8 COLLATE utf8_bin DEFAULT NULL
  4. `A` varchar(32) CHARACTER SET utf8 COLLATE utf8_bin DEFAULT NULL
  5. `S` varchar(64) CHARACTER SET utf8 COLLATE utf8_bin DEFAULT NULL
  6. `F` tinyint(4) DEFAULT NULL
  7. `V` varchar(256) CHARACTER SET utf8 COLLATE utf8_bin DEFAULT ''
  8. `N` varchar(64) CHARACTER SET utf8 COLLATE utf8_bin DEFAULT NULL
  9. KEY `IX_L` (`L`), 
  10. KEY `IX_I` (`I`), 
  11. KEY `IX_S` (`S`) 
  12. ) ENGINE=InnoDB DEFAULT CHARSET=utf8; 
  13.  
  14. CREATE TABLE `b` ( 
  15. `R` timestamp NOT NULL DEFAULT '2000-01-01 00:00:00'
  16. `V` varchar(32) DEFAULT NULL
  17. `U` varchar(32) DEFAULT NULL
  18. `C` varchar(16) DEFAULT NULL
  19. `S` varchar(64) DEFAULT NULL
  20. `I` varchar(64) DEFAULT NULL
  21. `E` bigint(32) DEFAULT NULL
  22. `ES` varchar(128) DEFAULT NULL
  23. KEY `IX_R` (`R`), 
  24. KEY `IX_C` (`C`), 
  25. KEY `IX_S` (`S`) 
  26. ) ENGINE=InnoDB DEFAULT CHARSET=utf8; 

從語句看,這個查詢計劃很自然的,就應該是先用a作為驅動表,先后使用 a.L和b.S這兩個索引。而實際上explain的結果卻是:

 

 
  1. +----+-------------+-------+-------+---------------+------+---------+----------+---------+-------------+ 
  2. | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | 
  3. +----+-------------+-------+-------+---------------+------+---------+----------+---------+-------------+ 
  4. | 1 | SIMPLE | b | index | IX_S | IX_S | 195 | NULL | 1038165 | Using index | 
  5. | 1 | SIMPLE | a | ref | IX_L,IX_S | IX_S | 195 | test.b.S | 1 | Using where | 
  6. +----+-------------+-------+-------+---------------+------+---------+----------+---------+-------------+ 

分析

從explain的結果看,查詢用了b作為驅動表。

上一篇文章我們介紹到,MySQL選擇jion順序是分別分析各種join順序的代價后,選擇最小代價的方法。

這個join只涉及到兩個表,自然也與optimizer_search_depth無關。于是我們的問題就是,我們預期的那個join順序的為什么沒有被選中?

MySQL Tips: MySQL提供straight_join語法,強制設定連接順序。

 

 
  1. explain SELECT count(*) FROM a straight_join b ON a.`S` = b.`S` WHERE a.`L` > '2014-03-30 00:55:00' AND a.`L` < '2014-03-30 01:00:00' ; 
  2.  
  3. +----+-------------+-------+-------+---------------+------+---------+------+---------+---------------------------------------------+ 
  4.  
  5. | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | 
  6.  
  7. +----+-------------+-------+-------+---------------+------+---------+------+---------+---------------------------------------------+ 
  8.  
  9. | 1 | SIMPLE | a | range | IX_L,IX_S | IX_L | 4 | NULL | 63 | Using where | 
  10.  
  11. | 1 | SIMPLE | b | index | IX_S | IX_S | 195 | NULL | 1038165 | Using where; Using index; Using join buffer | 
  12.  
  13. +----+-------------+-------+-------+---------------+------+---------+------+---------+---------------------------------------------+ 

MySQL Tips: explain結果中,join的查詢代價可以用依次連乘rows估算。

?join順序對了,簡單的分析查詢代價:普通join是1038165*1, straight_join是 63*1038165. 貌似MySQL沒有錯。但一定哪里不對!

發現異常

回到我們最初的設想。我們預計表a作為驅動表,是因為認為表b能夠用上IX_S索引,而實際上staight_join的時候確實用上了,但這個結果與我們預期的又不同。

我們知道,索引的過濾性是決定了一個索引在查詢中是否會被選中的重要因素,那么是不是b.S的過濾性不好呢?

MySQL Tips: show index from tbname返回結果中Cardinality的值可以表明一個索引的過濾性。

show index的結果太多,也可以從information_schema表中取。

 

 
  1. mysql> select * from information_schema.STATISTICS where table_name='b' and index_name='IX_S'/G 
  2. *************************** 1. row *************************** 
  3. TABLE_CATALOG: def 
  4. TABLE_SCHEMA: test 
  5. TABLE_NAME: b 
  6. NON_UNIQUE: 1 
  7. INDEX_SCHEMA: test 
  8. INDEX_NAME: IX_S 
  9. SEQ_IN_INDEX: 1 
  10. COLUMN_NAME: S 
  11. COLLATION: A 
  12. CARDINALITY: 1038165 
  13. SUB_PART: NULL 
  14. PACKED: NULL 
  15. NULLABLE: YES 
  16. INDEX_TYPE: BTREE 
  17. COMMENT: 
  18. INDEX_COMMENT: 

可以這個索引的CARDINALITY: 1038165,已經很大了。那這個表的估算行是多少呢。

 

 
  1. show table status like 'b'/G 
  2. *************************** 1. row *************************** 
  3. Name: b 
  4. Engine: InnoDB 
  5. Version: 10 
  6. Row_format: Compact 
  7. Rows: 1038165 
  8. Avg_row_length: 114 
  9. Data_length: 119160832 
  10. Max_data_length: 0 
  11. Index_length: 109953024 
  12. Data_free: 5242880 
  13. Auto_increment: NULL 
  14. Create_time: 2014-05-23 00:24:25 
  15. Update_time: NULL 
  16. Check_time: NULL 
  17. Collation: utf8_general_ci 
  18. Checksum: NULL 
  19. Create_options: 
  20. Comment: 
  21. 1 row in set (0.00 sec) 

從Rows: 1038165看出,IX_S這個索引的區分度被認為非常好,已經近似于唯一索引。

MySQL Tips: 在show table status結果中看到的Rows用于表示表的當前行數。對于MyISAM表這是一個精確值,但對InnoDB這是個估算值。

雖然是估算值,但優化器是以此為指導的,也就是說,上面的某個explain里面的數據完全不符合期望:staight_join結果中第二行的rows。

階段結論

我們發現整個錯誤的邏輯是這樣的:以a為驅動表的執行計劃,由于索引b.S的rows估計為1038165導致優化器認為代價大于以b為驅動表。而實際上這個索引的區分度為1.(當然對explan結果比較熟悉的同學會發現,第二行的type字段和Extra字段一起詭異了)

也就是說,straight_join得到的每一行去b中查詢的時候,都走了全表掃描。在MySQL里面出現這種情況的最常見的是類型轉換。比如一個字符串字段,雖然包含的是全數字,但查詢的時候傳入的不是字符串格式。

在這個case里面,兩個都是字符串。因此,就是字符集相關了。

回到兩個表結構,發現S字段的聲明差別在于 COLLATE utf8_bin -- 這個就是本case的根本原因了:a表得到的S值是utf8_bin,優化器認為類型不同,無法直接用上索引b.IX_S過濾。

至于為什么還會用上索引,這個是因為覆蓋索引帶來“誤解”。

MySQL Tips:若查詢的所有結果能夠從某個索引完全得到,則會優先用遍歷索引替代遍歷數據。

作為驗證,

 

 
  1. mysql> explain SELECT * FROM a straight_JOIN b ON binary a.`S` = b.`S` WHERE a.`L` > '2014-03-30 00:55:00' AND a.`L` < '2014-03-30 01:00:00' ; 
  2.  
  3. +—-+————-+——-+——-+—————+——+———+——+———+————————————————+ 
  4.  
  5. | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | 
  6.  
  7. +—-+————-+——-+——-+—————+——+———+——+———+————————————————+ 
  8.  
  9. | 1 | SIMPLE | a | range | IX_L | IX_L | 4 | NULL | 63 | Using where | 
  10.  
  11. | 1 | SIMPLE | b | ALL | IX_S | NULL | NULL | NULL | 1038165 | Range checked for each record (index map: 0x4) | 
  12.  
  13. +—-+————-+——-+——-+—————+——+———+——+———+————————————————+ 

由于結果是select *, 無法使用覆蓋索引,因此第二行的key就顯示為NULL. (筆者淚:要是早出這個結果查起來可方便多了)

優化

當然最直接的想法就是修改兩個表的S字段的定義,改成相同即可。這個方法可以避免修改業務代碼,但DDL代價略大。這里提供兩種在SQL語句方面的優化。

 

  1. select count(*) from b join (select s from a WHERE a.`L` > '2014-03-30 00:55:00' AND a.`L` < '2014-03-30 01:00:00') ta on b.S=ta.s; 

這個寫法比較直觀,需要注意最后b.S和ta.S的順序

 

 
  1. SELECT count(*) FROM a JOIN b ON binary a.`S` = b.`S` WHERE a.`L` > '2014-03-30 00:55:00' AND a.`L` < '2014-03-30 01:00:00' ; 

從前面的分析知道是由于b.S定義為utf8_bin.

MySQL Tips: MySQL中字符集命名規則中, XXX_bin與XXX的區別為大小寫是否敏感。

這里我們將A.s全部增加binary限定,先轉為小寫,就是將臨時結果集轉成utf8_bin,之后使用b.S匹配時就能夠直接利用索引。

其實兩個改寫方法的本質相同,區別是寫法1是隱式轉換。理論上說寫法2速度更快些。

小結

做join的字段盡量設計為類型完全相同。

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
亚洲精品国产品国语在线| 日韩欧美国产视频| 日韩欧美在线视频观看| 国产亚洲一区二区精品| 欧美亚洲在线播放| 久久免费福利视频| 91久久精品美女| 国产精品91久久| 日韩av大片免费看| 欧美精品免费在线| 亚洲欧美综合v| 国模视频一区二区三区| 成人信息集中地欧美| www.欧美视频| 亚洲欧美国产高清va在线播| 日韩电影在线观看永久视频免费网站| 粉嫩老牛aⅴ一区二区三区| 精品激情国产视频| 国产一区二区三区在线播放免费观看| 尤物yw午夜国产精品视频| 亚洲free性xxxx护士白浆| 亚洲一区二区三区视频| 57pao成人永久免费视频| 欧美日韩在线免费观看| 亚洲在线观看视频网站| 日韩不卡中文字幕| 成人精品一区二区三区| 日韩电影免费观看在线观看| 欧美精品videosex牲欧美| 91精品国产91久久久久久久久| 亚洲国产精品久久91精品| 国产日韩精品一区二区| 欧美成人全部免费| 一区二区三区精品99久久| 岛国av一区二区| 亚洲国产精品久久精品怡红院| 久久久伊人日本| 国产在线观看不卡| 丝袜情趣国产精品| 亚洲天堂av网| 91久久夜色精品国产网站| 综合欧美国产视频二区| 热久久免费视频精品| 亚洲变态欧美另类捆绑| 日本一区二区三区在线播放| 尤物九九久久国产精品的特点| 欧美精品日韩www.p站| 亚洲free性xxxx护士hd| 黑人巨大精品欧美一区二区三区| 性欧美激情精品| 亚洲美女免费精品视频在线观看| 欧美精品一区二区三区国产精品| 亚洲精品v欧美精品v日韩精品| 日韩av男人的天堂| 国产精品精品视频| 国产欧美一区二区三区久久| 日韩av免费看| 97视频免费观看| 欧美黑人性猛交| 成人综合网网址| 成人美女免费网站视频| 欧洲美女7788成人免费视频| 欧美性在线观看| 国产精品久久久av| 亚洲天堂男人的天堂| 午夜精品国产精品大乳美女| 欧美日本精品在线| 成人国产精品一区二区| 日韩一区二区三区xxxx| www.欧美三级电影.com| 亚洲乱码一区av黑人高潮| 中文字幕在线国产精品| 欧美大尺度激情区在线播放| 91po在线观看91精品国产性色| 日韩av高清不卡| 亚洲精品自拍视频| 国产成人免费av电影| 国产精品嫩草影院一区二区| 美女视频久久黄| 不卡毛片在线看| 欧美精品在线免费播放| 国产精品久久久久久久久久久新郎| 亚洲永久免费观看| 一本一本久久a久久精品综合小说| 久久久久久一区二区三区| 成人av在线网址| 亚洲美女激情视频| 日本午夜在线亚洲.国产| 精品一区电影国产| 国产亚洲a∨片在线观看| 欧美成人精品三级在线观看| 亚洲美女免费精品视频在线观看| 欧美大胆在线视频| 97在线精品视频| 欧美性高跟鞋xxxxhd| 奇米成人av国产一区二区三区| 国产精品香蕉av| 欧美性猛交xxxx免费看久久久| 欧美成在线观看| 国产欧美韩国高清| 亚洲高清不卡av| 久久久精品2019中文字幕神马| 精品久久久在线观看| 国产成人一区二区| 日韩高清av一区二区三区| 成人亲热视频网站| 亚洲欧美一区二区激情| 91在线免费看网站| 国产精品三级美女白浆呻吟| 性金发美女69hd大尺寸| 亚洲国产精彩中文乱码av在线播放| 91精品国产成人| 亚洲视频在线播放| 日韩成人小视频| 国产一区二区三区在线免费观看| 国产一区私人高清影院| 在线观看精品自拍私拍| 国外视频精品毛片| 国产欧亚日韩视频| 97国产真实伦对白精彩视频8| 欧美中文在线观看| 国产精品久久久久久久天堂| 成人信息集中地欧美| 亚洲精品aⅴ中文字幕乱码| 日韩中文字幕在线| 成人欧美一区二区三区在线湿哒哒| 国内精品在线一区| 日本成熟性欧美| 国产成人精品日本亚洲| 亚洲成人三级在线| 91在线观看免费高清完整版在线观看| 国产成人精品在线视频| 亚洲乱亚洲乱妇无码| 色综合男人天堂| 国产成人福利夜色影视| 欧美日韩一区二区免费视频| 亚洲嫩模很污视频| 国产欧美精品一区二区| 日韩电视剧在线观看免费网站| 国产伦精品一区二区三区精品视频| 欧美国产高跟鞋裸体秀xxxhd| 久久精品在线播放| 成人av番号网| 国产精品视频久久久久| 色伦专区97中文字幕| 福利视频一区二区| 日韩视频免费大全中文字幕| 中文字幕在线亚洲| 日韩成人小视频| 69影院欧美专区视频| 中文字幕一区二区三区电影| 热久久免费视频精品| 伊人激情综合网| 国产午夜精品免费一区二区三区| 国产一区二区三区在线免费观看| 国产成人精品久久亚洲高清不卡| 欧美精品九九久久| 在线亚洲午夜片av大片| 亚洲区一区二区| 亚洲一级黄色av| 国产精选久久久久久| 人妖精品videosex性欧美| 国产91色在线| 国产精品免费一区|