久久精品人人爽,华人av在线,亚洲性视频网站,欧美专区一二三

MySQL為什么有時候會選錯索引

145次閱讀
沒有評論

共計 17133 個字符,預計需要花費 43 分鐘才能閱讀完成。

本篇內容介紹了“MySQL 為什么有時候會選錯索引”的有關知識,在實際案例的操作過程中,不少人都會遇到這樣的困境,接下來就讓丸趣 TV 小編帶領大家學習一下如何處理這些情況吧!希望大家仔細閱讀,能夠學有所成!

今天在生產環境中看到一個慢 SQL,是個核心業務表,數據 1300 萬 +

看一下表索引:

mysql show index from `order`
+-----------------+----------------------+--------------------+------------------------+-----------------------+---------------------+-----------------------+--------------------+------------------+----------------+----------------------+-------------------+-------------------------+
| Table | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment |
+-----------------+----------------------+--------------------+------------------------+-----------------------+---------------------+-----------------------+--------------------+------------------+----------------+----------------------+-------------------+-------------------------+
| order | 0 | PRIMARY | 1 | id | A | 10493505 | | | | BTREE | | |
| order | 0 | uidx_order | 1 | order_seq | A | 10512924 | | | | BTREE | | |
| order | 1 | idx_user | 1 | user_id | A | 1995181 | | | YES | BTREE | | |
| order | 1 | idx_shop | 1 | shop_id | A | 53933 | | | YES | BTREE | | |
| order | 1 | idx_out_channel | 1 | out_channel | A | 524 | | | YES | BTREE | | |
| order | 1 | idx_out_channel | 2 | out_order_no | A | 10512924 | | | YES | BTREE | | |
| order | 1 | idx_order_time | 1 | order_time | A | 9867734 | | | | BTREE | | |
| order | 1 | idx_update_time | 1 | update_time | A | 8305698 | | | | BTREE | | |
| order | 1 | idx_create_time | 1 | create_time | A | 9951390 | | | | BTREE | | |
+-----------------+----------------------+--------------------+------------------------+-----------------------+---------------------+-----------------------+--------------------+------------------+----------------+----------------------+-------------------+-------------------------+
返回行數:[9],耗時:4 ms.
mysql SELECT id,order_seq,user_id 
 `ORDER`
WHERE
 delete_flag = 0 
 AND user_id =  d4b0c318b28a46968718dddbaf4775c0  
 AND display_status = 2 
ORDER BY
 order_time asc 
 LIMIT 0,20
+--------------+---------------------+----------------------------------+
| id | order_seq | user_id |
+--------------+---------------------+----------------------------------+
| 6068129 | 20161128183300861 | d4b0c318b28a46968718dddbaf4775c0 |
| 6118611 | 20161206171509550 | d4b0c318b28a46968718dddbaf4775c0 |
| 6885081 | 20170427104933189 | d4b0c318b28a46968718dddbaf4775c0 |
| 7720299 | 2017101718252243 | d4b0c318b28a46968718dddbaf4775c0 |
| 10319613 | 201905281103186182 | d4b0c318b28a46968718dddbaf4775c0 |
| 505498 | 2019082116584284235 | d4b0c318b28a46968718dddbaf4775c0 |
| 10840144 | 1119082315041792571 | d4b0c318b28a46968718dddbaf4775c0 |
+--------------+---------------------+----------------------------------+
返回行數:[7],耗時:18534 ms.

耗時 18s,這個查詢速度肯定是不能接受的。

我們看一下執行計劃:

mysql EXPLAIN SELECT id,order_seq,user_id 
 `ORDER`
WHERE
 delete_flag = 0 
 AND user_id =  d4b0c318b28a46968718dddbaf4775c0  
 AND display_status = 2 
ORDER BY
 order_time asc 
 LIMIT 0,20
+--------------+-----------------------+-----------------+----------------------+----------------+-------------------------+----------------+-------------------+---------------+----------------+--------------------+-----------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+--------------+-----------------------+-----------------+----------------------+----------------+-------------------------+----------------+-------------------+---------------+----------------+--------------------+-----------------+
| 1 | SIMPLE | ORDER | | index | idx_user | idx_order_time | 5 | | 2705 | 0.01 | Using where |
+--------------+-----------------------+-----------------+----------------------+----------------+-------------------------+----------------+-------------------+---------------+----------------+--------------------+-----------------+

  執行計劃中看到,這個 SQL 走索引 idx_order_time,根據經驗判斷,此索引效率很差。而掃描行數為 2705,慢日志顯示掃描行數為 13,347,074,二者相差甚遠,那么為什么會出現如此大的差異呢?

        選擇索引是優化器的工作。而優化器選擇索引的目的,是找一個最優的執行方案,并用最小的代價去執行語句。在數據庫里面,掃描行數是影響執行代價的因素之一。掃描的行數越少,意味著訪問磁盤數據的次數越少,消耗的 CPU 資源越少。

        當然,掃描行數并不是唯一的判斷標準,優化器還會結合是否使用臨時表,是否排序等因素進行綜合判斷。掃描行數是怎么判斷的?

        MySQL 在真正執行 SQL 之前,并不能準確的判斷滿足這個條件的數據有多少行,只能按統計信息來估算行數。

索引的統計信息就是索引的“區分度”,一個索引不同的值越多,這個索引的區分度就越好,而一個索引上不同的值的個數,我們稱之為“基數”,基數越大,索引的區分度越好。

若強制使用 idx_user 索引,看下執行情況:

mysql SELECT id,order_seq,user_id 
 `ORDER` force index(idx_user)
WHERE
 delete_flag = 0 
 AND user_id =  d4b0c318b28a46968718dddbaf4775c0  
 AND display_status = 2 
ORDER BY
 order_time asc 
 LIMIT 0,20
+--------------+---------------------+----------------------------------+
| id | order_seq | user_id |
+--------------+---------------------+----------------------------------+
| 6068129 | 20161128183300861 | d4b0c318b28a46968718dddbaf4775c0 |
| 6118611 | 20161206171509550 | d4b0c318b28a46968718dddbaf4775c0 |
| 6885081 | 20170427104933189 | d4b0c318b28a46968718dddbaf4775c0 |
| 7720299 | 2017101718252243 | d4b0c318b28a46968718dddbaf4775c0 |
| 10319613 | 201905281103186182 | d4b0c318b28a46968718dddbaf4775c0 |
| 505498 | 2019082116584284235 | d4b0c318b28a46968718dddbaf4775c0 |
| 10840144 | 1119082315041792571 | d4b0c318b28a46968718dddbaf4775c0 |
+--------------+---------------------+----------------------------------+

查詢速度還是很快的,看一下執行計劃:

mysql explain SELECT id,order_seq,user_id 
 `ORDER` force index(idx_user)
WHERE
 delete_flag = 0 
 AND user_id =  d4b0c318b28a46968718dddbaf4775c0  
 AND display_status = 2 
ORDER BY
 order_time asc 
 LIMIT 0,20
+--------------+-----------------------+-----------------+----------------------+----------------+-------------------------+---------------+-------------------+---------------+----------------+--------------------+----------------------------------------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+--------------+-----------------------+-----------------+----------------------+----------------+-------------------------+---------------+-------------------+---------------+----------------+--------------------+----------------------------------------------------+
| 1 | SIMPLE | ORDER | | ref | idx_user | idx_user | 163 | const | 77706 | 1 | Using index condition; Using where; Using filesort |
+--------------+-----------------------+-----------------+----------------------+----------------+-------------------------+---------------+-------------------+---------------+----------------+--------------------+----------------------------------------------------+
返回行數:[1],耗時:4 ms.

如果換成數據行數少一些的 user_id

mysql EXPLAIN SELECT id,order_seq,user_id 
 `ORDER` 
WHERE
 delete_flag = 0 
 AND user_id =  1e41c833fc6f4f57b490a4627a4170dc  
 AND display_status = 2 
ORDER BY
 order_time asc 
 LIMIT 0,20
+--------------+-----------------------+-----------------+----------------------+----------------+-------------------------+---------------+-------------------+---------------+----------------+--------------------+----------------------------------------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+--------------+-----------------------+-----------------+----------------------+----------------+-------------------------+---------------+-------------------+---------------+----------------+--------------------+----------------------------------------------------+
| 1 | SIMPLE | ORDER | | ref | idx_user | idx_user | 163 | const | 13 | 1 | Using index condition; Using where; Using filesort |
+--------------+-----------------------+-----------------+----------------------+----------------+-------------------------+---------------+-------------------+---------------+----------------+--------------------+----------------------------------------------------+
返回行數:[1],耗時:4 ms.
mysql SELECT id,order_seq,user_id 
 `ORDER` 
WHERE
 delete_flag = 0 
 AND user_id =  1e41c833fc6f4f57b490a4627a4170dc  
 AND display_status = 2 
ORDER BY
 order_time asc 
 LIMIT 0,20
+--------------+---------------------+----------------------------------+
| id | order_seq | user_id |
+--------------+---------------------+----------------------------------+
| 10397123 | 2019092523044218361 | 1e41c833fc6f4f57b490a4627a4170dc |
+--------------+---------------------+----------------------------------+
返回行數:[1],耗時:4 ms.

對比一下兩個 user_id 對應的數據量:

 返回行數:[1],耗時:4 ms.
mysql select count(*) from order01 where user_id= 1e41c833fc6f4f57b490a4627a4170dc 
+--------------------+
| count(*) |
+--------------------+
| 15 |
+--------------------+
返回行數:[1],耗時:4 ms.
mysql select count(*) from order01 where user_id= d4b0c318b28a46968718dddbaf4775c0 
+--------------------+
| count(*) |
+--------------------+
| 38611 |
+--------------------+
返回行數:[1],耗時:14 ms.

總結:在此業務場景中,MySQL 優化器認為檢索 38000 行數據然后進行排序要比檢索 15 行數據排序代價大得多,所以選擇了有序的索引 idx_order_time,但未必是最快的執行計劃。

但是,此處還有一個疑問,如果對于 user_id:d4b0c318b28a46968718dddbaf4775c0,不使用 limit 分頁,執行計劃是什么樣呢?

mysql SELECT * 
 ORDER01 
WHERE
 delete_flag = 0 
 AND user_id =  d4b0c318b28a46968718dddbaf4775c0 
 AND display_status = 2 
ORDER BY
 order_time desc
+--------------+---------------------+----------------------+----------------------+----------------------------------+-----------------------+---------------------+----------------------------------+---------------------+----------------------+---------------------+-------------------------------+-------------------------+---------------------------+-----------------------------------+-----------------------+-------------------------+-------------------+-----------------------+------------------------+--------------------------+----------------------+---------------------+--------------------------+------------------------+------------------------+---------------------+------------------+-----------------------+-----------------------+-----------------------+
| id | order_seq | order_type | order_flag | user_id | user_mobile | user_nick | shop_id | shop_name | pay_status | pay_time | receiver_address_id | receiver_name | receiver_mobile | receiver_address | cancel_time | cancel_reason | channel | out_channel | out_order_no | out_store_name | order_time | over_time | display_status | order_status | sale_channel | sale_mode | remark | delete_flag | create_time | update_time |
+--------------+---------------------+----------------------+----------------------+----------------------------------+-----------------------+---------------------+----------------------------------+---------------------+----------------------+---------------------+-------------------------------+-------------------------+---------------------------+-----------------------------------+-----------------------+-------------------------+-------------------+-----------------------+------------------------+--------------------------+----------------------+---------------------+--------------------------+------------------------+------------------------+---------------------+------------------+-----------------------+-----------------------+-----------------------+
| 11153421 | 201911091339555506 | 1 | 1 | d4b0c318b28a46968718dddbaf4775c0 | 13718903545 |  總部 - 客服 - 補單  | 29e541d6da9b4aae8957409ca03c6670 |  清悠  | 1 | 2019-11-09 13:40:10 | 2666265 |  總部 - 客服 - 補單  | 13718903545 |  東城區   王府井   王府井   總部補單  | | 0 | | 0 | 201911091339555506 | | 2019-11-09 13:39:55 | | 2 | 200 | 1 | 1 | | 0 | 2019-11-09 13:39:55 | 2019-11-09 13:40:10 |
| 7720299 | 2017101718252243 | 1 | 1 | d4b0c318b28a46968718dddbaf4775c0 | 13718903545 |  總部 - 客服 - 補單  | ad41dba7bf5c4b69b03e0222878cb2b0 |  蝶舞  | 1 | 2017-10-17 18:25:26 | 2282099 |  總部 - 客服 - 補單  | 13718903545 | 2 號線;  地鐵 7 號線   華強北   總部補單  | | 0 | | 0 | 2017101718252243 | | 2017-10-17 18:25:22 | | 2 | 200 | 1 | 1 | | 0 | 2017-10-17 18:25:22 | 2017-10-17 18:25:22 |
| 6885081 | 20170427104933189 | 1 | 1 | d4b0c318b28a46968718dddbaf4775c0 | 13718903545 |  總部 - 客服 - 補單  | c6092260f92643098f7f56e68560d8c0 |  木蘭花  | 1 | 2017-04-27 10:49:39 | 2264946 |  總部 - 客服 - 補單  | 13718903545 |  天河北商圈  | | 0 | | 0 | 20170427104933189 | | 2017-04-27 10:49:33 | | 2 | 200 | 1 | 1 | | 0 | 2017-04-27 10:49:33 | 2017-04-27 10:49:33 |
| 6118611 | 20161206171509550 | 1 | 1 | d4b0c318b28a46968718dddbaf4775c0 | 13718903545 |  總部 - 客服 - 補單  | 7a0cd4d60f52423fb757b0be1ab55be6 |  娟子  | 1 | 2016-12-06 17:15:12 | 1904075 |  總部 - 客服 - 補單  | 13718903545 |  廣東省深圳市南山區深南大道   科技園  | | 0 | helijia | 0 | 20161206171509550 | | 2016-12-06 17:15:09 | | 2 | 200 | 1 | 1 | | 0 | 2016-12-06 17:15:09 | 2016-12-06 17:15:09 |
| 6068129 | 20161128183300861 | 1 | 1 | d4b0c318b28a46968718dddbaf4775c0 | 13718903545 |  總部 - 客服 - 補單  | f6f4612493654695ac4c6bac6df67672 |  美天  | 1 | 2016-11-28 18:33:03 | 1544109 |  總部 - 客服 - 補單  | 13718903545 |  青羊區金河路口寬窄巷子   寬窄巷子  | | 0 | helijia | 0 | 20161128183300861 | | 2016-11-28 18:33:00 | | 2 | 200 | 1 | 1 | | 0 | 2016-11-28 18:33:00 | 2016-11-28 18:33:00 |
+--------------+---------------------+----------------------+----------------------+----------------------------------+-----------------------+---------------------+----------------------------------+---------------------+----------------------+---------------------+-------------------------------+-------------------------+---------------------------+-----------------------------------+-----------------------+-------------------------+-------------------+-----------------------+------------------------+--------------------------+----------------------+---------------------+--------------------------+------------------------+------------------------+---------------------+------------------+-----------------------+-----------------------+-----------------------+
返回行數:[5],耗時:152 ms.
mysql explain SELECT * 
 ORDER01 
WHERE
 delete_flag = 0 
 AND user_id =  d4b0c318b28a46968718dddbaf4775c0 
 AND display_status = 2 
ORDER BY
 order_time desc
+--------------+-----------------------+-----------------+----------------------+----------------+-------------------------+---------------+-------------------+---------------+----------------+--------------------+----------------------------------------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+--------------+-----------------------+-----------------+----------------------+----------------+-------------------------+---------------+-------------------+---------------+----------------+--------------------+----------------------------------------------------+
| 1 | SIMPLE | ORDER01 | | ref | idx_user | idx_user | 163 | const | 75800 | 1 | Using index condition; Using where; Using filesort |
+--------------+-----------------------+-----------------+----------------------+----------------+-------------------------+---------------+-------------------+---------------+----------------+--------------------+----------------------------------------------------+
返回行數:[1],耗時:4 ms.

查詢速度很快,執行計劃走了 user_id 字段的索引。為什么會出現這樣的情況呢?

查閱了相關資料,對于 order by limit 這樣的排序,當檢索到的數據較多的時候,排序消耗是很大的,這個時候由于優化器選擇了有序的 idx_order_time 而導致執行索引選擇錯誤。

優化辦法:

1、強制使用索引 idx_user;

2、創建組合索引 idx_uid_ordertime(user_id,order_time)

mysql alter table `ORDER` add index idx_uid_ordertime(user_id,order_time)
執行成功,耗時:60334 ms.
mysql SELECT id,order_seq,user_id 
 `ORDER` 
WHERE
 delete_flag = 0 
 AND user_id =  d4b0c318b28a46968718dddbaf4775c0  
 AND display_status = 2 
ORDER BY
 order_time asc 
 LIMIT 0,20
+--------------+---------------------+----------------------------------+
| id | order_seq | user_id |
+--------------+---------------------+----------------------------------+
| 6068129 | 20161128183300861 | d4b0c318b28a46968718dddbaf4775c0 |
| 6118611 | 20161206171509550 | d4b0c318b28a46968718dddbaf4775c0 |
| 6885081 | 20170427104933189 | d4b0c318b28a46968718dddbaf4775c0 |
| 7720299 | 2017101718252243 | d4b0c318b28a46968718dddbaf4775c0 |
| 10319613 | 201905281103186182 | d4b0c318b28a46968718dddbaf4775c0 |
| 505498 | 2019082116584284235 | d4b0c318b28a46968718dddbaf4775c0 |
| 10840144 | 1119082315041792571 | d4b0c318b28a46968718dddbaf4775c0 |
+--------------+---------------------+----------------------------------+
返回行數:[7],耗時:86 ms.
mysql explain SELECT id,order_seq,user_id 
 `ORDER` 
WHERE
 delete_flag = 0 
 AND user_id =  d4b0c318b28a46968718dddbaf4775c0  
 AND display_status = 2 
ORDER BY
 order_time asc 
 LIMIT 0,20
+--------------+-----------------------+-----------------+----------------------+----------------+----------------------------+-------------------+-------------------+---------------+----------------+--------------------+------------------------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+--------------+-----------------------+-----------------+----------------------+----------------+----------------------------+-------------------+-------------------+---------------+----------------+--------------------+------------------------------------+
| 1 | SIMPLE | ORDER | | ref | idx_user,idx_uid_ordertime | idx_uid_ordertime | 163 | const | 72772 | 1 | Using index condition; Using where |
+--------------+-----------------------+-----------------+----------------------+----------------+----------------------------+-------------------+-------------------+---------------+----------------+--------------------+------------------------------------+
返回行數:[1],耗時:4 ms.

“MySQL 為什么有時候會選錯索引”的內容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業相關的知識可以關注丸趣 TV 網站,丸趣 TV 小編將為大家輸出更多高質量的實用文章!

正文完
 
丸趣
版權聲明:本站原創文章,由 丸趣 2023-07-28發表,共計17133字。
轉載說明:除特殊說明外本站除技術相關以外文章皆由網絡搜集發布,轉載請注明出處。
評論(沒有評論)
主站蜘蛛池模板: 浦北县| 平乐县| 清涧县| 淮北市| 石柱| 公安县| 宣威市| 泰和县| 潢川县| 湟源县| 和政县| 新邵县| 惠安县| 瑞安市| 河北省| 德阳市| 全南县| 陵川县| 萨嘎县| 新昌县| 万州区| 桃源县| 楚雄市| 鄂托克旗| 洪雅县| 安国市| 翁牛特旗| 夏津县| 大英县| 通州区| 镶黄旗| 延庆县| 武定县| 千阳县| 丹凤县| 天等县| 余江县| 视频| 河池市| 抚远县| 和政县|