記一次Innodb慢查詢分析過程

2021-09-11 12:48:54 字數 4507 閱讀 2904

結論區分度低的索引到底會帶來哪方面的影響?

#資料量約700w

create table `order` (

`orderid` varchar(32) not null comment '訂單編號',

`type` char(1) default '0' comment '訂單型別:0 流量訂單,1 話費訂單',

`caller` varchar(16) not null comment '手機號碼',

`status` smallint(6) not null default '0' comment '-1下單失敗、0初始、1待支付、2支付超時、3支付成功、4充值中、5充值成功、6充值失敗 7充值超時 8退款中 9退款失敗 10退款成功',

`ordertime` datetime default current_timestamp comment '訂單下單的時間',

`biztype` varchar(6) default null comment '應用下業務標識',

primary key (`orderid`),

key `index_orderid` (`orderid`),

key `index_status` (`status`),

key `index_caller` (`caller`),

key `index_ordertime` (`ordertime`),

key `index_type` (`type`)

) engine=innodb default charset=utf8 row_format=compact comment='訂單表';

複製**

#耗時約25s

select

count( * ) as col_0_0_

from

`order` orderentit0_

where

1 = 1

and orderentit0_.type = '0'

and status = '5'

and ordertime >= '2017-08-01 16:17:00'

and ordertime <= '2017-08-30 16:17:00';

複製**

從上面的ddl我們可以看到能利用上的索引欄位有:

使用explain命令來進行分析

可以看到利用了status,type兩個欄位的索引,使用了索引合併(同時走兩個索引,取交集)

這邊有幾個疑問?

show index from order

重點關注cardinality字段,翻譯為基數,可以理解為該索引字段不同結果的總數,用於粗略判斷索引區分度(數值越大越好)

可以判斷type,status兩個欄位的索引的區分度十分的低

所以通過這兩個索引欄位會取出一大堆資料,再一一對ordertime欄位進行過濾,效率自然就低了

我們使用force index ( index_ordertime ) 來強制走ordertime索引

explain

select

count( * ) as col_0_0_

from

`order` orderentit0_ force

index ( index_ordertime )

where

1 = 1

and orderentit0_.type = '0'

andstatus = '5'

and ordertime >= '2017-08-01 16:17:00'

and ordertime <= '2017-08-30 16:17:00'

;複製**

來看explain分析結果

通過強制走ordertime索引方式,耗時約4s

結合之前的explain結果做個對比

索引type

keyrows

type,status

index_merge

index_type,index_status

58wordertime

range

index_ordertime

96w 可以看到因為sql優化器判斷通過走合併index_status和index_type比走index_ordertime索引需要檢索的行數少,所以選擇了走索引合併

# 實際結果約47w,執行時間0.4s

select

count( * ) as col_0_0_

from

`order` orderentit0_ force index ( index_ordertime )

where

1 = 1

and ordertime >= '2017-08-01 16:17:00'

and ordertime <= '2017-08-30 16:17:00'

# 實際結果約23w,執行時間8s

select

count( * ) as col_0_0_

from

`order` orderentit0_

where

1 = 1

and orderentit0_.type = '0'

and status = '5'

複製**

至此兩個問題解釋完畢,鼓掌鼓掌?

但是又帶來第三個問題....

1.索引合併,覆蓋

#23w,約3.5s

select count( * ) as col_0_0_

from `order` orderentit0_

where 1 = 1

and orderentit0_.type = '0'

and status = '5'

explain:

index_merge index_status,index_type null 587752

2.ordertime索引,覆蓋

#47w,約0.8s

select

count( * ) as col_0_0_

from

`order` orderentit0_

where

1 = 1

and ordertime >= '2017-08-01 16:17:00'

and ordertime <= '2017-08-30 16:17:00'

explain:

range index_ordertime null 963112

3.索引合併,非覆蓋

#23w,約22s

from `order` orderentit0_

where 1=1

and orderentit0_.type = '0'

and status = '5'

explain:

同14.ordertime索引,非覆蓋

#47w,約4s

select

from

`order` orderentit0_

force index ( index_ordertime )

where

1 = 1

and ordertime >= '2017-08-01 16:17:00'

and ordertime <= '2017-08-30 16:17:00'

explain:

同25.status索引,覆蓋

#37w,約0.3s

select count( * ) as col_0_0_

from `order` orderentit0_

where 1 = 1

and status = '5'

6.type索引,覆蓋

#400w,約1.7s

select count( * ) as col_0_0_

from `order` orderentit0_

where 1 = 1

and orderentit0_.type = '0'

複製**

猜測?:

由於type欄位索引區分度太低,導致多餘的磁碟io時間

對ordertime索引的查詢型別為range,優先順序較低

本人水平有限,在此丟擲問題,請各位大神賜教

刪除type,status,ordertime索引(前二區分度太低),

新增ordertime,status,type的聯合索引(基於業務需求)

**********===2018-04-09***************===

對於問題三的找到理論依據了

記一次慢查詢引發的事故

首先,測試環境上線新版本,並且通過黑盒測試以及功能測試。然後,我們就上線了新的版本。但是在執行3天後,整個伺服器大部分介面都失效了,基本上都是timeout。檢查伺服器情況 cpu基本上佔滿了。接著查了資料庫狀態,通過mysql命令show processlist 存在大量的waiting for ...

記一次Mongodb聚合查詢 慢查詢簡單分析

記一次mongodb聚合查詢 一 慢查詢 db.public setting detail.aggregate isdir 1,status 1 createtime 執行分析結果 1 fields queryplanner winningplan direction forward rejecte...

記一次sql查詢

效果圖 要查詢出如上圖的效果 知識點.1.多表巢狀查詢.2.輸出查詢結果,group concat函式 3.關聯查詢 select t1.學校,case when t1.年級 2017 then 1年級 when t1.年級 2016 then 2年級 when t1.年級 2015 then 3年...