記一次SqlServer大表查詢語句優化和執行計劃分析 T-SQL查詢高級--理解SQL SERVER中非聚集索引的覆蓋,連接,交叉和過濾 MSSQLSERVER執行計劃詳解

數據庫: sqlserver2008r2 

表: device_data

數據量:2000w行左右

表結構


CREATE TABLE [dbo].[device_data](
    [Id] [int] IDENTITY(1,1) NOT NULL,
    [DeviceId] [char](12) NOT NULL,
    [SystemTick] [int] NOT NULL,
    [Sport] [int] NOT NULL,
    [Temperature] [int] NOT NULL,
    [Voltage] [int] NOT NULL,
    [UploadTime] [datetime] NOT NULL,
    [CollectorMac] [char](8) NOT NULL,
 CONSTRAINT [PK__device_d__3214EC0770FDBF69] PRIMARY KEY CLUSTERED 
(
    [Id] ASC
)WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]
) ON [PRIMARY]

 索引情況:分別有兩個聯合索引

idx_deviceid(DeviceId,UploadTime)

idx_collector(CollectorMac,UploadTime)

問題:這張表上傳的數據都是隨上傳時間遞增,批量有序插入進去。但是 最近幾天日誌經常出現插入數據超時,然後去分析了一下數據庫,發現對外接口出現了慢 sql導致了死鎖,

慢sql是where條件在某些情況下沒有加上時間篩選過濾,2000萬的表導致非常慢,於是進行了優化,優化的時候變出現了下面的兩條sql語句

這兩個索引對應着兩個接口 查詢語句分別是:

 select top 100 Id,DeviceId,Sport,Temperature,CollectorMac,UploadTime from device_data 
 where [DeviceId]='C9C810B18272' and UploadTime>'2020-12-01 15:16:55.000' order by UploadTime desc;
和
 select top 100 Id,DeviceId,Sport,Temperature,CollectorMac,UploadTime from device_data 
 where [CollectorMac]='95DE5F0B' and UploadTime>'2020-12-01 15:16:55.000' order by UploadTime desc;

現在遇到一個情況是 相同的where條件下 如果我Order by Id Desc 就會發現返回結果中會比order by UploadTime desc慢一點,我一開始不太理解爲啥會出現這個情況。

於是想到了sqlserver的查詢分析器來分析一下具體的sql執行計劃

 

1,首先我來執行order by UploadTime desc的語句來看一下具體的執行計劃

dbcc dropcleanbuffers
set statistics io on
select top 100 Id,DeviceId,Sport,Temperature,CollectorMac,UploadTime from device_data 
where [CollectorMac]='95DE5F0B' and UploadTime>'2020-12-01 15:16:55.000' order by UploadTime desc;
set statistics io off

具體執行計劃爲:

DBCC 執行完畢。如果 DBCC 輸出了錯誤信息,請與系統管理員聯繫。

(100 行受影響)
表 'device_data'。掃描計數 1,邏輯讀取 1899 次,物理讀取 5 次,預讀 24 次,lob 邏輯讀取 0 次,lob 物理讀取 0 次,lob 預讀 0 次。

(1 行受影響)

 

 2,再來看一下order by Id desc的具體執行計劃

dbcc dropcleanbuffers
set statistics io on
select top 100 Id,DeviceId,Sport,Temperature,CollectorMac,UploadTime from device_data 
where [CollectorMac]='95DE5F0B' and UploadTime>'2020-12-01 15:16:55.000' order by Id desc;
set statistics io off

具體執行計劃爲:

DBCC 執行完畢。如果 DBCC 輸出了錯誤信息,請與系統管理員聯繫。

(100 行受影響)
表 'device_data'。掃描計數 1,邏輯讀取 40 次,物理讀取 36 次,預讀 4942 次,lob 邏輯讀取 0 次,lob 物理讀取 0 次,lob 預讀 0 次。

(1 行受影響)

 

這裏先說一下邏輯讀取和物理讀取等區別

那麼,這幾個詞語代表什麼意思呢?我們怎麼根據這些來了解SQL語句或者存儲過程的I/O過程呢?

預讀:用於估計信息,去硬盤讀取數據到緩存。

物理讀:查詢計劃生成以後,如果發現緩存缺少所需要的數據,讓緩存再次去讀硬盤數據。如果內存裏沒有緩存數據或者執行計劃(如果SQL語句發生了改變,

那麼執行計劃將不能重用,需要重新生成新的執行計劃),那麼SQLSERVER就要去硬盤讀取這些數據,這個時候就是物理讀取,我們大家都知道,硬盤速度

與內存速度根本不在一個數量級上,所以物理讀是比較慢的。

邏輯讀:SQLSERVER去內存裏的緩存取數據或者執行計劃,所以邏輯讀是比較快的。

SQLSERVER存儲的最小單位是頁,每一頁大小爲8K,即8*1024=8192字節,SQLSERVER對頁的讀取是原子性的,即要麼讀完一頁,要麼完全不讀。即使

僅僅要獲得一條數據,也要讀完該頁,而頁之間的數據組織結構爲B樹結構。所以SQLSERVER對於邏輯讀,物理讀,預讀的單位是頁。

 

可以看到bytime 走的是索引查找和鍵查找 並且大部分讀取走的是邏輯讀取

byid 走的是聚集索引掃描,並且大部分讀取走的是預讀取

看上面的介紹我們發現 邏輯讀取走的是內存緩存,預讀取是去硬盤讀取數據到緩存然後再邏輯讀取,在這一步我們基本確定了 byid慢的原因是讀取了硬盤數據

那麼上圖出現的索引查找和索引掃描這兩者又有什麼區別呢 

查看了相關資料發現

Clustered Index Scan(聚集索引掃描)、Index Scan(非聚集索引掃描)

 

 

聚集索引掃描:聚集索引的數據體積實際是就是表本身,也就是說表有多少行多少列,聚集所有就有多少行多少列,那麼聚集索引掃描就跟表掃描差不多,也要進行全表掃描,遍歷所有表數據,查找出你想要的數據。

非聚集索引掃描:非聚集索引的體積是根據你的索引創建情況而定的,可以只包含你要查詢的列。那麼進行非聚集索引掃描,便是你非聚集中包含的列的所有行進行遍歷,查找出你想要的數據。

 

Clustered Index Seek(聚集索引查找)、Index Seek(非聚集索引查找)

 

 

聚集索引查找和非聚集索引查找都是使用該圖標。

聚集索引查找:聚集索引包含整個表的數據,也就是在聚集索引的數據上根據鍵值取數據。

非聚集索引查找:非聚集索引包含創建索引時所包含列的數據,在這些非聚集索引的數據上根據鍵值取數據。

Key Lookup(鍵值查找)

 

 

首先需要說的是查找,查找與掃描在性能上完全不是一個級別的,掃描需要遍歷整張表,而查找只需要通過鍵值直接提取數據,返回結果,性能要好。

當你查找的列沒有完全被非聚集索引包含,就需要使用鍵值查找在聚集索引上查找非聚集索引不包含的列。

 

我們發現byid引起全量掃描了 所以會慢很多

疑問:我的理解是既然我已經加了where條件去篩選數據了 order by Id還是 order by Uploadtime 是不是應該在我where篩選出來的數據中再去排序,爲啥只是因爲Orderby的不同,最終的執行計劃差別這麼大

我對理論方面不深入,只能從現象來解決問題,還請大佬們賜教

參考資料:

T-SQL查詢高級--理解SQL SERVER中非聚集索引的覆蓋,連接,交叉和過濾

MSSQLSERVER執行計劃詳解

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章