延續前文:[SQL Server] Parameter Sniffing: OPTION OPTIMIZE FOR UNKNOWN
使用 OPTIMIZE FOR UNKNOWN
- 如果無法識別出 常用 的 parameter 值,或是 Query Optimizer 使用此 parameter 值所 compile 的 Execution Plan 效率不佳(例如:由於 data skew 數據傾斜)。
- SQL 開發人員 設定 Query Optimizer 僅使用 Average Distribution (平均分配) 機制來執行 cardinality estimation。
- 至於輸入什麼 parameter,回傳多少筆數,讓 Query Optimizer 都不要列入考量,也不使用 Parameter Sniffing。
- 在此次範例中,甚至連該有的 Missing Index 建議也不見了!
評估方式
OPTIMIZE FOR UNKNOWN / Local variables
Conditions | Query Optimizer only use |
equality: = | Rows * All density |
inequality: > , < , etc., | Rows * 30% |
當條件式是:
- 使用 equality (等號),則 Query Optimizer 僅使用 density vector 來執行 cardinality estimation,也就是: Rows * All density。
- 使用 Inequality (不相等),則 Query Optimizer 連 density vector 也不用了,就直接設定資料列總數的 30% 來執行 cardinality estimation,也就是: Rows * 30% 。
試想:
- 這種方式建立出來的 Execution Plan 會有好的效能嗎?
- 這會是 效能最佳化 的 Execution Plan 嗎?
OPTION OPTIMIZE FOR UNKNOWN 2
延續前文:[SQL Server] Parameter Sniffing: OPTION OPTIMIZE FOR UNKNOWN
01. 執行 Stored Procedure,輸入 Parameter: @StateProvinceID= 32
- Input Parameter: StateProvinceID= 32
- Use OPTION (OPTIMIZE FOR UNKNOWN);
- Actual Number of Rows 是: 1。
- 但評估 Estimate Number of Rows 卻是: 265.054 ?
Why is "Estimated Number of Rows" get 265.054 ?
-- figure 01_Why is "Estimated Number of Rows" get 265.054 ?
02. 由 SSMS 的 GUI 介面,觀察 Execution Plan
- StatementEstRows="265.054"
- OptimizeStatsUsage
在 OptimizeStatsUsage,可以看是 Statistics: [IX_Address_StateProvinceID]。
也就是說,Query Optimizer 是使用 Index: [IX_Address_StateProvinceID] 的 statistics (統計資料),執行 Query Optimization。
-- figure 11_OptimizeStatsUsage
03. Show Execution Plan XML,可以到相關的資訊。
-- figure 12_XML_OptimizeStatsUsage
04. 使用 DBCC SHOW_STATISTICS,顯示目前的 query optimization 的 statistics。
- Query Optimizer 使用 statistics 來預估 Cardinality (基數) 或 查詢結果中的資料列數,如此可讓 Query Optimizer 建立高品質的 Execution Plan。
- 舉例來說,Query Optimizer 可使用 Cardinality Estimates (CE) 估來選擇 Execution Plan 中的 index seek operator,而不是 index scan operator,避免發生 resource-intensive index scan 來提高查詢效能。
Query Optimizer 使用 statistics 物件中的相關資料來計算 Cardinality Estimates (CE) 。
DBCC SHOW_STATISTICS 可以顯示儲存在 statistics 物件中的資料,包含:
- Header (標頭)
- Density Vector (密度向量)
- Histogram (長條圖)
Header (標頭)
- 有關 statistics 的 metadata 。
Histogram (長條圖)
- 顯示 first key column (第一個資料行) 中的 distribution of values (值分佈狀態)。
Density Vector (密度向量)
- 用來測量 cross-column correlation (跨資料行關聯)。
- Density 是資料行或組合資料行中的 duplicates 重複項目數量之資訊,其計算方式為 1/(distinct values 數目)。
-- figure 13_Displays Query Optimization statistics
05. 觀察 DBCC SHOW_STATISTICS 所顯示的資料。
- 在 Header(標頭)
- Rows: 19614
- 在 Density Vector (密度向量)
- All density: 0.01351351
Density 是資料行或組合資料行中的 duplicates 重複項目數量之資訊,其計算方式為 1/(distinct values 數目)。
StateProvinceID 的 All density: 0.01351351,可以使用以下方式檢驗:
-- figure 13_01_All density
謎底揭曉:
- 0.01351351 * 19614 = 265.05398514,約等於 265.054。
-- figure 14_Rows_Density
由上得知,當使用 OPTION (OPTIMIZE FOR UNKNOWN) :
- 且 條件式 使用 equality (等號, = )
- Query Optimizer 一律都 僅 使用 Rows * All density 方式來評估
也就是說,輸入什麼 parameter,回傳多少筆數,Query Optimizer 都不列入考量。
還記得嗎?在本次範例中,連該有的 Missing Index 建議也不見了!
試想:
- 這種方式建立出來的 Execution Plan 會有好的效能嗎?
- 這會是 效能最佳化 的 Execution Plan 嗎?
06. 在 Histogram (長條圖),接近 265.054 rows 的是:
- RANGE_HI_KEY: 66
- EQ_ROWS: 242
也就是
- StateProvinceID: 66
- Return: 242 rows
-- figure 17_RANGE_HI_KEY = 66
07. 改用 Ad hoc Query,條件式 StateProvinceID= 66:
- Return: 242 rows
- Index Scan: [IX_Address_AddressLine1_AddressLine2_City_StateProvinceID_PostalCode]
-- figure 21_StateProvinceID_66_Execution_Plan
08. 觀察 效能 STATISTICS 統計資訊: CPU, Disk IO 等
- Number of Rows Read: 19614
- Actual Number of Rows: 242
- Estimated Number of Rows: 242
- Estimated Number of Rows to be Read: 19614
- LastElapsedTime(sec): 0.001405000
- LastCPUTime(sec)]: 0.001404000
- LastRows: 242
- EstimatedNumberofRows: 242
- Last_logical_reads: 216
-- figure 22_Performance_Statistics
Change condition: StateProvinceID = 163
01. 改用 Ad hoc Query,條件式 StateProvinceID= 163:
- Return: 62 rows
- Index Seek: [IX_Address_StateProvinceID]
當 StateProvinceID = 163,Query Optimizer 評估使用 Index Seek - Good Execution Plan.
-- figure 23_StateProvinceID_163_Execution_Plan
02. 觀察 效能 STATISTICS 統計資訊: CPU, Disk IO 等
- Number of Rows Read: 62
- Actual Number of Rows: 62
- Estimated Number of Rows: 62
- Estimated Number of Rows to be Read: 62
- LastElapsedTime(sec): 0.066149000
- LastCPUTime(sec)]: 0.000820000
- LastRows: 62
- EstimatedNumberofRows: 62
- Last_logical_reads: 150
-- figure 24_Performance_Statistics
03. 使用 SQL 查詢式,觀察 StateProvinceID: 66
- RowCount: 242
-- figure 31_GROUP BY StateProvinceID_66
04. 使用 SQL 查詢式,觀察 StateProvinceID: 163
- RowCount: 62
-- figure 32_GROUP BY StateProvinceID_163
Return 74 group
- There are 53 categories of StateProvinceID to use use Index Seek
- There are 21 categories of StateProvinceID to use use Index Scan
Use Inequality Operators, 使用 不相等
- Equality Operators (=)
- Inequality Operators 是指使用:> (Greater Than) , < (Less Than) 等等。
01. 建立新的 Stored Procedure: up_OPTION_OPTIMIZE_FOR_UNKNOWN_Inequality,使用 Inequality Operators。
- OPTION (OPTIMIZE FOR UNKNOWN);
- Use < (Less Than) Operators
-- figure 41_inequality
02. 執行 Stored Procedure,輸入 Parameter: @StateProvinceID= 2
- Number of Rows Read: 19614
- Actual Number of Rows: 25
- Estimated Number of Rows: 5884.2
- Estimated Number of Rows to be Read: 19614
Use OPTION (OPTIMIZE FOR UNKNOWN),使用 < (Less Than)
- return 25 rows
- 但為什麼 EstimatedNumberofRows 卻是 5884.2 ?
為什麼是使用 EstimatedNumberofRows: 5884.2 來評估建立 Execution Plan 呢?
-- figure 42_StateProvinceID_2_Execution_Plan
02. 觀察 效能 STATISTICS 統計資訊: CPU, Disk IO 等
- LastRows: 25
- EstimatedNumberofRows: 5884.2
- Last_logical_reads: 218
-- figure 43_Performance_Statistics
03. 再度更換 Parameter: @StateProvinceID= 181
- Number of Rows Read: 19614
- Actual Number of Rows: 19583
- Estimated Number of Rows: 5884.2
- Estimated Number of Rows to be Read: 19614
Use OPTION (OPTIMIZE FOR UNKNOWN),使用 < (Less Than)
- return 19583 rows
- 但為什麼 EstimatedNumberofRows 卻是 5884.2 ?
怎麼又是使用 EstimatedNumberofRows: 5884.2 來評估建立 Execution Plan 呢?
-- figure 44_StateProvinceID_181_Execution_Plan
04. 觀察 效能 STATISTICS 統計資訊: CPU, Disk IO 等
- LastRows: 19583
- EstimatedNumberofRows: 5884.2
- Last_logical_reads: 218
-- figure 45_Performance_Statistics
03. 觀察 DBCC SHOW_STATISTICS 所顯示的資料
- 在 Header(標頭)
- Rows: 19614
謎底就是:
- Inequality: EstimatedNumberofRows is 30% of the rows
- 19614*0.3 = 5884.2
-- figure 46_Inequality_EstimatedNumberofRows is 30 of the rows
當使用 OPTION (OPTIMIZE FOR UNKNOWN) :
- 且 條件式 使用 Inequality Operators :> (Greater Than) , < (Less Than) 等等。
- Query Optimizer 一律都 僅 使用 Rows * 30% 方式來評估
也就是說,輸入什麼 parameter,回傳多少筆數,Query Optimizer 都不列入考量。
試問:
- 這種方式建立出來的 Execution Plan 會有好的效能嗎?
- 這會是 效能最佳化 的 Execution Plan 嗎?
Sample Code
20180114_OPTION_OPTIMIZE_FOR_UNKNOWN_2
https://drive.google.com/drive/folders/0B9PQZW3M2F40OTk3YjI2NTEtNjUxZS00MjNjLWFkYWItMzg4ZDhhMGIwMTZl?usp=sharing
參考資料
[SQL Server] Parameter Sniffing: OPTION OPTIMIZE FOR UNKNOWN
http://sharedderrick.blogspot.tw/2018/01/sql-server-parameter-sniffing-option_7.html
沒有留言:
張貼留言