日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > 数据库 >内容正文

数据库

sql server排序慢_SQL 查询调优之 where 条件排序字段以及 limit 使用索引的奥秘

發布時間:2024/7/5 数据库 32 豆豆
生活随笔 收集整理的這篇文章主要介紹了 sql server排序慢_SQL 查询调优之 where 条件排序字段以及 limit 使用索引的奥秘 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

(給數據分析與開發加星標,提升數據技能)

作者:風過無痕-唐

www.cnblogs.com/tangyanbo/p/6378741.html

奇怪的慢sql

我們先來看2條sql

第一條:

select * from acct_trans_log WHERE acct_id = 1000000000009000757 order by create_time desc limit 0,10

第二條:

select * from acct_trans_log WHERE acct_id = 1000000000009003061 order by create_time desc limit 0,10

表的索引及數據總情況:

索引:acct_id,create_time 分別是單列索引,數據庫總數據為500w。

通過 acct_id 過濾出來的結果集在 1w 條左右。

查詢結果:第一條要5.018s,第二條0.016s

為什么會是這樣的結果呢?第一,acct_id和create_time都有索引,不應該出現5s查詢時間這么慢啊

那么先來看執行計劃

第一條sql執行計劃:

?第二條執行計劃:

?仔細觀察會發現,索引只使用了idx_create_time,沒有用到idx_acct_id。

這能解釋第一條sql很慢,因為where查詢未用到索引,那么第二條為什么這么快?

看起來匪夷所思,其實搞清楚mysql查詢的原理之后,其實很簡單。

我們來看這2條sql查詢,都用到了where order by limit。

當有limit存在時,查詢的順序就有可能發生變化,這時并不是從數據庫中先通過where過濾再排序再limit。

因為如果這樣的話,從500萬數據中通過where過濾就不會是5s了。

此時的執行順序是,先根據idx_create_time索引樹,從最右側葉子節點,反序取出n條,然后逐條去跟where條件匹配。

若匹配上,則得出一條數據,直至取滿10條為止,為什么第二條sql要快,因為運氣好,剛好時間倒序的前幾條就全部滿足了。

搞清楚原理之后,我們了解了為什么第一條慢,第二條快的原因,但是問題又來了

為什么mysql不用idx_acct_id索引,這是一個問題,因為這樣的話,我們的建立的索引基本失效了,在此類sql下查詢效率將會是相當低。

因為通過acct_id過濾出來的結果集比較大,有上萬條,mysql認為按時間排序如果不用索引,將會是filesort,這樣會很慢,而又不能2個索引都用上,所以選擇了idx_create_time。

為什么mysql只用一個索引

這里為什么不能2個索引都用上,可能很多人也不知道為什么,其實道理很簡單,每個索引在數據庫中都是一個索引樹,其數據節點存儲了指向實際數據的指針,如果用一個索引來查詢,其原理就是從索引樹上去檢索,并獲得這些指針,然后去取出數據,試想,如果你通過一個索引,得到過濾后的指針,這時,你的另一個條件索引如果再過濾一遍,將得到2組指針的集合,如果這時候取交集,未必就很快,因為如果每個集合都很大的話,取交集的時候,等于掃描2個集合,效率會很低,所以沒法用2個索引。當然有時候mysql會考慮臨時建立一個聯合索引,將2個索引聯合起來用,但是并不是每種情況都能奏效,同樣的道理,用一個索引檢索出結果集之后,排序時,也無法用上另一個索引了。

實際上用索引idx_acct_id大多數情況還是要比用索引idx_create_time要快,我們舉個例子:

select * from acct_trans_log force index(idx_acct_id) WHERE acct_id = 1000000000009000757 order by create_time desc limit 0,10

耗時:0.057s

可以看出改情況用idx_acct_id索引是比較快的,那么是不是這樣就可以了呢,排序未用上索引,始終是有隱患的。

聯合索引讓where和排序字段同時用上索引

我們來看下一條sql:

select * from acct_trans_log force index(idx_acct_id) WHERE acct_id = 3095 order by create_time desc limit 0,10

耗時: 1.999s

執行計劃:

?該sql通過acct_id過濾出來的結果集有100萬條,因此排序將會耗時較高,所幸這里只是取出前10條最大的然后排序

查詢概況,我們發現時間基本消耗在排序上,其實這是內存排序,對內存消耗是很高的。

那么我們有沒有其它解決方案呢,這種sql是我們最常見的,如果處理不好,在大數據量的情況下,耗時以及對數據庫資源的消耗都很高,這是我們所不能接受的,我們的唯一解決方案就是讓where條件和排序字段都用上索引

解決辦法就是建立聯合索引:

alter table acct_trans_log add index idx_acct_id_create_time(acct_id,create_time)

然后執行sql:

select * from acct_trans_log WHERE acct_id = 3095 order by create_time desc limit 0,10

耗時: 0.016s

聯合索引讓where條件字段和排序字段都用上了索引,問題解決了!

聯合索引使用的原理

但是為什么能解決這個問題呢,這時大家可能就會記住一個死理,就是聯合索引可以解決where過濾和排序的問題,也不去了解其原理,這樣是不對的,因為當情況發生變化,就懵逼了,

下面我們再看一個sql:

select * from acct_trans_log force index(idx_acct_id_create_time) WHERE acct_id in(3095,1000000000009000757) order by create_time desc limit 0,10

耗時:1.391s

索引還是用idx_acct_id_create_time,時間居然慢下來了。

執行計劃是:

?

?看執行計劃,排序用到了filesort,也就是說,排序未用到索引。

那么我們還是來看看,索引排序的原理,我們先來看一個sql:

select * from acct_trans_log ORDER BY create_time limit 0,100

耗時:0.029s

執行計劃為:

?這里執行的步驟是,先從索引樹中,按時間升序取出前100條,因為索引是排好序的,直接左序遍歷即可了,因此,這里mysql并沒有做排序動作,如果想降序,則右序遍歷索引樹,取出100條即可,查詢固然快,那么聯合索引的時候,是怎樣的呢?

select?*?from?acct_trans_log?WHERE??acct_id?=?3095?order by create_time desc limit 0,10

使用組合索引:idx_acct_id_create_time。

這個時候,因為acct_id是聯合索引的前綴,因此可以很快實行檢索,如果sql是

select * from acct_trans_log WHERE acct_id = 3095

出來的數據是按如下邏輯排序的

3095+time13095+time23095+time3

默認是升序的,也就是說,次sql相當于

select * from acct_trans_log WHERE acct_id = 3095 order by create_time

他們是等效的。

如果我們把條件換成order by create_time desc limit 0,10呢?

這時候,應該從idx_acct_id_create_time樹右邊葉子節點倒序遍歷,取出前10條即可

因為數據的前綴都是3095,后綴是時間升序。那么我們倒序遍歷出的數據,剛好滿足 order by create_time desc。因此也無需排序。

那么語句:

select * from acct_trans_log force index(idx_acct_id_create_time) WHERE acct_id in(3095,1000000000009000757) order by create_time desc limit 0,10

為什么排序無法用索引呢?

我們先分析下索引的排序規則,

已知:id1

查詢結果集排序如下:

id1+time1

id1+time2

id1+time3

id2+time1

id2+time2

id2+time3

索引出來的默認排序是這樣的,id是有序的,時間是無序的,因為有2個id,優先按id排序,時間就是亂的了,這樣排序將會用filesort,這就是慢的原因,也是排序沒有用到索引的原因。

查詢計劃使用以及使用說明

table:顯示這一行數據是關于哪張表的

type:顯示使用了何種類型,從最好到最差的連接類型為const,eq_ref,ref,range,index,all

possible_keys:顯示可能應用在這張表中的索引。如果為空,沒有可能的索引

key:實際使用的索引,如果為null,則沒有使用索引。

key_len:使用的索引的長度。在不損失精確性的情況下,長度越短越好

ref:顯示索引的哪一列被使用了,如果可能的話,是一個常數

rows:mysql認為必須檢查的用來返回請求數據的行數

推薦閱讀??點擊標題可跳轉記一次神奇的 SQL 查詢經歷,group by 慢查詢優化淺談 MySQL 中優化 SQL 語句查詢常用的 30 種方法MySQL 百萬級數據量分頁查詢方法及其優化

看完本文有收獲?請轉發分享給更多人

關注「數據分析與開發」加星標,提升數據技能

好文章,我在看??

總結

以上是生活随笔為你收集整理的sql server排序慢_SQL 查询调优之 where 条件排序字段以及 limit 使用索引的奥秘的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。