ORDER BY導致索引使用不理想

来源:https://www.cnblogs.com/gjc592/archive/2019/11/22/11909656.html
-Advertisement-
Play Games

在MySQL中經常出現未按照理想情況使用索引的情況,今天記錄一種Order by語句的使用導致未按預期使用索引的情況。 1. 問題現象 1.1 SQL語句: SELECT DISTINCT p.* FROM tb_name p WHERE 1=1 AND p.createDate >= '2019- ...


在MySQL中經常出現未按照理想情況使用索引的情況,今天記錄一種Order by語句的使用導致未按預期使用索引的情況。

1.  問題現象

1.1 SQL語句:

SELECT DISTINCT p.*  FROM tb_name p 
WHERE 1=1 AND p.createDate >= '2019-10-23'  AND p.createDate <= '2019-11-20 24:00:00'  AND p.status = '1'  AND p.areaName LIKE  '%上海%'  
 ORDER BY p.payDate DESC LIMIT 0 , 15

1.2 執行計劃如下:

+----+-------------+-------+------------+-------+-------------------------------------------------------------+--------------------+---------+------+--------+----------+------------------------------------+
| id | select_type | table | partitions | type  | possible_keys                                               | key                | key_len | ref  | rows   | filtered | Extra                              |
+----+-------------+-------+------------+-------+-------------------------------------------------------------+--------------------+---------+------+--------+----------+------------------------------------+
|  1 | SIMPLE      | p     | NULL       | range | createDate,idx_status_payDate                   | idx_status_payDate | 108     | NULL | 880063 |     0.74 | Using index condition; Using where |
+----+-------------+-------+------------+-------+-------------------------------------------------------------+--------------------+---------+------+--------+----------+------------------------------------+

1.3 表中索引信息如下:

+------------------+------------+-------------------------------+--------------+--------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| Table            | Non_unique | Key_name                      | Seq_in_index | Column_name  | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment |
+------------------+------------+-------------------------------+--------------+--------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| tb_name          |          0 | PRIMARY                       |            1 | id           | A         |     1760103 |     NULL | NULL   |      | BTREE      |         |               |
| tb_name          |          1 | idx_payDate                   |            1 | payDate      | A         |     1734626 |     NULL | NULL   | YES  | BTREE      |         |               |
| tb_name          |          1 | createDate                    |            1 | createDate   | A         |     1736316 |     NULL | NULL   | YES  | BTREE      |         |               |
| tb_name          |          1 | idx_status_payDate            |            1 | status       | A         |           2 |     NULL | NULL   | YES  | BTREE      |         |               |
| tb_name          |          1 | idx_status_payDate            |            2 | payDate      | A         |     1741214 |     NULL | NULL   | YES  | BTREE      |         |               |
+------------------+------------+-------------------------------+--------------+--------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
16 rows in set (0.00 sec)

1.4   理想情況

運行此SQL耗時約5.7s。從SQL及索引情況來看,使用createDate欄位的索引應該會更好才對,為驗證此情況,使用force index來強制使用createDate索引運行一次查看結果。

SQL改為如下:

SELECT DISTINCT p.*  FROM tb_name p  FORCE INDEX (createDate)
WHERE 1=1 AND p.createDate >= '2019-10-23'  AND p.createDate <= '2019-11-20 24:00:00'  AND p.status = '1'  AND p.areaName LIKE  '%上海%'  
ORDER BY p.payDate DESC LIMIT 0 , 15

修改後執行計劃如下:

root@db09:03:13>explain SELECT DISTINCT p.*  FROM tb_namep  FORCE INDEX (createDate)
    -> WHERE 1=1 AND p.createDate >= '2019-10-23'  AND p.createDate <= '2019-11-20 24:00:00'  AND p.status = '1'  AND p.areaName LIKE  '%上海%'  
    ->  ORDER BY p.payDate DESC LIMIT 0 , 15;
+----+-------------+-------+------------+-------+---------------+------------+---------+------+--------+----------+----------------------------------------------------+
| id | select_type | table | partitions | type  | possible_keys | key        | key_len | ref  | rows   | filtered | Extra                                              |
+----+-------------+-------+------------+-------+---------------+------------+---------+------+--------+----------+----------------------------------------------------+
|  1 | SIMPLE      | p     | NULL       | range | createDate    | createDate | 6       | NULL | 117858 |     1.11 | Using index condition; Using where; Using filesort |
+----+-------------+-------+------------+-------+---------------+------------+---------+------+--------+----------+----------------------------------------------------+
1 row in set, 3 warnings (0.00 sec)

實際運行該SQL耗時約為0.15s,相差約50倍的差距。

1.5 簡單分析

從執行計劃情況對比來看,使用createDate會進行額外的排序(Using filesort),這個不難理解。

 

2   各種不太合理嘗試

2.1 強制使用索引

使用force  index (createDate)是可以解決的,此方式上面已經測試過了

2.2  忽略不理想的索引

類似於force index,可以使用IGNORE INDEX ,其實目的也在於使用上createDate 索引,例如:

 

SELECT DISTINCT p.*  FROM tb_name p  IGNORE INDEX (idx_status_payDate,idx_payDate)
WHERE 1=1 AND p.createDate >= '2019-10-23'  AND p.createDate <= '2019-11-20 24:00:00'  AND p.status = '1'  AND p.areaName LIKE  '%上海%'  
ORDER BY p.payDate DESC LIMIT 0 , 15

 

其效果和force index 一致,運行耗時也在0.15s左右。

2.3 添加組合索引

將payDate 及createDate 添加為組合索引,但是此舉不是一個好辦法,執行計劃也未按理想情況運行。

 

3.  相對合理的方式

無論使用force  index  還是 ignore index都會影響MySQL優化器自身的執行情況。例如createDate 如果範圍很大,那麼其實走payDate 的索引取前15條記錄會更快,為了讓應用改動最少且不會因為其他條件的變化而導致未能走合理的索引,選擇另一種優化方案,將SQL改為如下情況:

SELECT DISTINCT p.*  FROM tb_name p 
WHERE 1=1 AND p.createDate >= '2019-10-23'  AND p.createDate <= '2019-11-20 24:00:00'  AND p.status = '1'  AND p.areaName LIKE  '%上海%'  
 ORDER BY p.payDate DESC, createDate LIMIT 0 , 15

此時執行執行計劃如下:

+----+-------------+-------+------------+-------+-------------------------------+------------+---------+------+--------+----------+----------------------------------------------------+
| id | select_type | table | partitions | type  | possible_keys                 | key        | key_len | ref  | rows   | filtered | Extra                                              |
+----+-------------+-------+------------+-------+-------------------------------+------------+---------+------+--------+----------+----------------------------------------------------+
|  1 | SIMPLE      | p     | NULL       | range | createDate,idx_status_payDate | createDate | 6       | NULL | 123024 |     5.55 | Using index condition; Using where; Using filesort |
+----+-------------+-------+------------+-------+-------------------------------+------------+---------+------+--------+----------+----------------------------------------------------+
1 row in set, 3 warnings (0.00 sec)

調整createDate 之後,執行執行計劃:

root@db 09:51:00>EXPLAIN 
    -> SELECT DISTINCT p.*  FROM tb_name p   IGNORE INDEX (idx_status_synIs_deleteStatus)
    -> WHERE 1=1 AND p.createDate >= '2009-10-23'  AND p.createDate <= '2019-11-20 24:00:00'  AND p.status = '1'  AND p.areaName LIKE  '%上海%'  
    ->  ORDER BY p.payDate DESC,createDate DESC  LIMIT 0 , 15;
+----+-------------+-------+------------+------+-------------------------------+--------------------+---------+-------+--------+----------+----------------------------------------------------+
| id | select_type | table | partitions | type | possible_keys                 | key                | key_len | ref   | rows   | filtered | Extra                                              |
+----+-------------+-------+------------+------+-------------------------------+--------------------+---------+-------+--------+----------+----------------------------------------------------+
|  1 | SIMPLE      | p     | NULL       | ref  | createDate,idx_status_payDate | idx_status_payDate | 108     | const | 880205 |     5.56 | Using index condition; Using where; Using filesort |
+----+-------------+-------+------------+------+-------------------------------+--------------------+---------+-------+--------+----------+----------------------------------------------------+
1 row in set, 3 warnings (0.00 sec)

也按預期的情況正常。由此看來此方式相對之前的方案是最佳的。

 

 

 

 

 

 

 

 

 

 

 

 

 

 


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 下載鏈接:https://pan.baidu.com/s/1uPbBknyIebQRDt4k_RA58Q 提取碼:14zi 將下載文件進行解壓,我解壓位置為:D:\Program Files\mysql-5.7.28-winx64 在D:\Program Files\mysql-5.7.28-win ...
  • 1.我們使用緩存時的業務流程大概為: 當我們查詢一條數據時,先去查詢緩存,如果緩存有就直接返回,如果沒有就去查詢資料庫,然後返回。這種情況下就可能出現下麵的一些現象。 2.緩存穿透 2.1什麼是緩存穿透 緩存穿透是指查詢一個一定不存在的數據,由於緩存是不命中時被動寫的,並且出於容錯考慮,如果從存儲層 ...
  • 如果你的系統有高併發的要求,可以嘗試使用SQL Server記憶體優化表來提升你的系統性能。你甚至可以把它當作Redis來使用。 ...
  • 對比結論 1. 性能上: 性能上都很出色,具體到細節,由於Redis只使用單核,而Memcached可以使用多核,所以平均每一個核上Redis在存儲小數據時比Memcached性能更高。而在100k以上的數據中,Memcached性能要高於Redis,雖然Redis最近也在存儲大數據的性能上進行優化 ...
  • select * from table1 t where (select count(*) from table1 where column1=t.column1 AND column2=t.column2 and column3=t.column3)>1 ...
  • USE [SPECIAL_BLD]GO SET ANSI_NULLS ONGO SET QUOTED_IDENTIFIER ONGO CREATE FUNCTION [dbo].[get_upper] ( @num numeric(18,5))RETURNS VARCHAR(500)ASBEGIN ...
  • 一、Atlas是什麼? 在當今大數據的應用越來越廣泛的情況下,數據治理一直是企業面臨的巨大問題。 大部分公司只是單純的對數據進行了處理,而數據的血緣,分類等等卻很難實現,市場上也急需要一個專註於數據治理的技術框架,這時Atlas應運而生。 Atlas官網地址: "https://atlas.apac ...
  • 看如下一條sql語句: # table T (id int, name varchar(20)) delete from T where id = 10; MySQL在執行的過程中,是如何加鎖呢? 在看下麵這條語句: select * from T where id = 10; 那這條語句呢?其實這 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...