簡體   English   中英

MySQL 查詢運行時間更好,即使它的執行計划很糟糕

[英]MySQL query run time is better even though its execution plan is bad

我正在嘗試優化這個 MySQL 查詢並且在理解執行計划方面經驗較少我很難理解執行計划。

我的問題是:您能否幫助我理解為什么New Query的查詢執行計划比Original query的差,即使 New Query 在 Prod 中執行得更好。

重現此案例所需的 SQL在這里
最后還保留了相關的表定義(表 bill_range 使用外鍵 bill_id 引用賬單)

在 PROD 中完成原始查詢需要 10 秒

select * 
from bill_range 
where (4050 between low and high ) 
order by bill_id limit 1;

新查詢(我強制/建議使用索引)在 PROD 中需要 5 秒才能完成

select * 
from bill_range 
use index ( bill_range_low_high_index) 
where (4050 between low and high ) 
order by bill_id limit 1;

但是執行計划給出了建議原始查詢更好(這是我的理解似乎錯誤的部分)

原始查詢在此處輸入圖像描述

新查詢在此處輸入圖像描述

  1. 原始查詢的列“類型”建議索引,而新查詢顯示全部
  2. 列“鍵”是原始查詢的bill_id (可能是 FK 上的索引)和新查詢的Null
  3. 原始查詢的列“行”為1 ,而新查詢為9

因此,鑒於所有這些信息,這並不意味着新查詢實際上比原始查詢更差。 如果這是真的,為什么新查詢的性能更好? 還是我讀錯了執行計划。

表定義

CREATE TABLE bill_range (
    id int(11) NOT NULL AUTO_INCREMENT,
    low varchar(255) NOT NULL,
    high varchar(255) NOT NULL,
    PRIMARY KEY (id),
    bill_id int(11) NOT NULL,
    FOREIGN KEY (bill_id) REFERENCES bill(id)
);
CREATE TABLE bill (
    id int(11) NOT NULL AUTO_INCREMENT,
    label varchar(10),
    PRIMARY KEY (id)
);
create index bill_range_low_high_index on bill_range( low, high);

注意:我提供 2 個表的定義的原因是因為原始查詢決定使用基於外鍵的索引來計費表

您的索引對於您的查詢來說不是很理想。 如果可以的話,讓我解釋一下。

MySQL 索引使用 BTREE 數據結構。 這些在索引順序訪問模式下運行良好(因此 MySQL 的第一個存儲引擎的 MyISAM 名稱)。 它支持跳轉到索引中特定位置然后逐個元素遍歷索引的查詢。 典型的例子是這樣,在col上有一個索引。

 SELECT whatever FROM tbl WHERE col >= constant AND col <= constant2

這是對WHERE col BETWEEN constant AND constant2的重寫。

讓我們重鑄您的查詢,這樣這個模式就很明顯了,所以您想要的列是明確的。

select id, low, high, bill_id 
from bill_range 
where low <= 4050 
  and high >= 4050 
order by bill_id limit 1;

high列上的索引允許從high >= 4050的第一個符合條件的行開始進行范圍掃描。 然后,我們可以對 go 做一個復合索引,包括bill_idlow列。

CREATE INDEX high_billid_low ON bill_range (high, bill_id, low);

因為我們想要最低匹配的bill_id ,所以我們接下來將其放入索引中,最后是low 所以查詢規划器隨機訪問索引到第一個符合條件high行,然后掃描直到找到第一個滿足low標准的索引項。 然后就完成了:這就是想要的結果。 它已經由bill_id訂購,因此可以停止。 ORDER BY來自索引。 查詢可以完全從索引中得到滿足——即所謂的覆蓋索引

至於為什么您的兩個查詢執行不同:首先,查詢計划程序決定按bill_id順序掃描您的數據,以尋找第一個匹配的low / high對。 可能它決定實際對結果集進行排序可能比按順序掃描bill_id更昂貴。 在我看來,您的第二個查詢進行了表掃描。 為什么會更快,誰知道呢?

請注意,此索引也適用於您。

CREATE INDEX low_billid_high ON bill_range (low DESCENDING, bill_id, high);

在 InnoDB 中,表的 PK id隱含地包含在每個索引中,因此無需在復合索引中提及它。

而且,您仍然可以按照第一次編寫它的方式編寫它; 查詢規划器會弄清楚你想要什么。

專業提示:避免SELECT * ... *使您更難推理您需要檢索的列。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM