非聚集索引中的臨界點(Tipping Point)

来源:http://www.cnblogs.com/Joe-T/archive/2016/06/30/5630270.html
-Advertisement-
Play Games

什麼是臨界點? 註意,我要說的問題是非聚集索引的執行計劃從Seek+Lookup變成Table/Clustered Index Scan的臨界點。SQL Server的訪問數據的IO最小單元是頁。 我們知道聚集索引的葉級是數據頁,非聚集索引的葉級是指向數據行的指針。所以通過聚集索引獲取數據時,就是直 ...


什麼是臨界點?

      註意,我要說的問題是非聚集索引的執行計劃從Seek+Lookup變成Table/Clustered Index Scan的臨界點。SQL Server的訪問數據的IO最小單元是頁。

      我們知道聚集索引的葉級是數據頁,非聚集索引的葉級是指向數據行的指針。所以通過聚集索引獲取數據時,就是直接訪問聚集索引本身,而通過非聚集索引獲取數據時,除了訪問自身,還要通過指針去訪問數據頁。這個過程就是RID/Key Lookup。而此Lookup是一個單頁操作,即每次使用一個RID/Key,然後去訪問對應的一個數據頁,然後獲取頁上的相應的數據行。可能當前數據頁的有多個數據行是符合查詢要求的,但是一次lookup,只能取當前的RID/Key指定的數據行。所以同一個數據頁,可能要被訪問很多次。例如,現在lookup要去找RID為2,3,5,7,9對應的數據行,而這5個數據行都存在數據頁N上,則數據頁N只少要被訪問5次。

    在Seek時,如果要返回N行數據,則Seek操作至少要訪問N次數據頁。當Lookup訪問次數據超過了全表數據頁的總數時,就會出現臨界點。這個時候Scan操作成本要比Lookup低。超過這個臨界點時,查詢優化器一般會選擇Scan替代Seek+Lookup。例如表T有100000行,每頁存放100行,共有1000頁。查詢1000條數據,理論/理想情況下:Scan最少時只需要10次IO,Lookup只少需要1000次IO。

    需要註意的是覆蓋索引中不存在RID/Key,而是對應的列值,所以不會出現這個問題。

 

臨界點什麼時候出現?

      前面說的理論和原理上的東西,而實際臨界點的出現由很多因素決定。但主要與表的總頁數相關臨界點大概出現在訪問頁占全表頁數的25%~33%時。為了直觀,通常把頁數再轉換成行數來分析。轉換時需要註意,前面闡明Lookup是單頁操作,所以頁數=行數

  • 一個表總行數為1,000,000,每頁存放2條行數,共500,000頁。則25%=125,000,33%=166,000。臨界點會出現在125,000頁和166,000頁間。轉換成行表示就是125000/(2*500000)=12.5%,166000/(2*500000)=16.6%。也就是說當返回行數小於62400(500000*12.55)時,很可能會使用Lookup。當返回行數大於83000時,很可能會使用Scan。這個表的行太寬了,一個頁只能存放2行數據,從百分比看起來沒有什麼太大感覺。
  • 一個表總行數為1,000,000,每頁存放100條行數,共10,000頁。則25%=2500,33%=3300。轉換成行2500/1000000=0.25%,3300/1000000=0.33%。它的臨界點上限不超過0.5%。也就是說你查詢表中不到0.5%的行數時,會全表掃描。
  • 一個表總行數為1,000,000,每頁存放20條行數,共50,000頁。則25%=125,00,33%=166,00。轉換成行表示就是125000/(2*500000)=1.25%,166000/(2*500000)=1.66%。

   不難發現,臨界點判斷,對於大表的查詢性能是有很大幫助的。而對於小表而言,幾乎都會是Scan,但是資料庫有緩存機制,小表會完整緩存,掃描影響也不大。

 

我們能做些什麼?

    很容易想到,既然表有Seek對應的索引,我們使用Hint強制使用Seek,問題不就解決了。這個不一定,本來這個問題的出現就是查詢優化器認為Scan比Lookup的成本要低。如果你強制可能會適得其反。SQL Server的查詢優化器是很強大和智能的,除非你嚴格測試過,證明ForceSeek性能更好一些。

 

示例分析

    使用AdventureWorks2012的Sales.SalesOrderDetail。在ProductID列有一個非聚集索引IX_SalesOrderDetail_ProductID。

通過下的查詢可以知道表有121317行,共1237個數據頁,每頁大約存放98行數據。由此我們可以預估一下臨界點在(309行,408行)附近。

select page_count,record_count
from sys.dm_db_index_physical_stats(db_id(),object_id(N'Sales.SalesOrderDetail'),1,null,'detailed')
where  index_level=0

然後再統計一下不同的ProductID在表中行數,好針對性的測試不同ProductID:

select ProductID,COUNT(*) as cnt
from Sales.SalesOrderDetail   
group by ProductID
order by cnt

通過上面查詢,我們知道ProductID=882在表中有407行,可以看到它還是使用Lookup的方式。它的IO計數為:

Table 'SalesOrderDetail'. Scan count 1, logical reads 1258

clipboard

 

ProductID=751在表中有409行,它就使用了Scan的方式。它的IO計數為:

Table 'SalesOrderDetail'. Scan count 1, logical reads 1246

clipboard[1]

 

我們還可以測試其它的返回行數更多的ProductID,如果是掃描的方式則IO都是在1246,如果是Lookup則都會高於1246。證明跟理論還是契合的。

就算500行返回才會超過臨界點,而500行也只占總行數的500/121317=0.41%。也就是說當返回行數超過全表的0.41%時,優化器就認為它的篩選度不夠高了,不用seek+lookup,要掃描了。

 

總結

1. 當遇到"明明有索引,為什麼會掃描?",臨界點的問題可能是原因之一。

2. 因為存在臨界點,所以非覆蓋非聚集索引的使用率可能沒有我們想象的高。

 

參考 

http://www.sqlskills.com/blogs/kimberly/the-tipping-point-query-answers/


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • PS:註意事項 一些坑在此聲明: 1、安裝Eclipse後,記得設置各項編碼格式為utf-8 請移步:http://www.blogjava.net/xiaomage234/archive/2014/05/13/413626.html(我也是網上查閱的~~) 2、下載Android SDK時需要翻牆 ...
  • 一、背景 很久之前就想寫一篇文章來記錄Cordova的使用了,上次用到它是在一年前,這次用到它是在這個月。下次用到它還不知道是什麼時候了,為了供以後翻閱,在此做個記錄。現在很多資料都是在硬碟里、網路收藏夾里。這次就一次性彙總到這裡吧。 二、簡介 Cordova提供了一組設備相關的API,通過這組AP ...
  • CocoaLumberjack是一個很好用的日誌列印工具,它可以幫助我們把工程中的日誌信息列印到終端或者輸出到文件中。 地址:https://github.com/CocoaLumberjack/CocoaLumberjack 類圖: 一:插件的運用 因為網上已經對CocoaLumberjack的運 ...
  • sql 的發佈訂閱,想必大家都瞭解,但一般都是在預設的1433的情況下搭建的,那麼1433換成別的埠,發佈還能正常工作嗎? 在一次客戶的真實場景上我就遇到了。 好了,今天不想寫太多,簡化下, 測試環境 發佈伺服器 電腦名 win-01 分發伺服器 電腦名 win-01 (與發佈用的同一臺) 訂 ...
  • 介紹 以前沒有太註意MySQL密碼安全策略的配置方法,只是人為了將密碼設為複雜密碼,但是沒有找到配置的方法,今天薑承堯的微信公眾號正好發佈了一篇關於這個的文章,所以在這裡也順便將方法寫下來。首先該功能是在5.5以後的mysql版本才引入的插件,預設源碼安裝和二進位安裝都沒有啟用該功能,如果沒有開啟該 ...
  • 今天在測試、驗證DROP_SNAPSHOT_RANGE不能徹底快照的過程中遇到了DROP_SNAPSHOT_RANGE無法清理WRM$_SNAPSHOT_DETAILS表中數據的情況,測試伺服器版本為10.2.0.5.0,AWR的快照是1小時採集一次數據,快照保留14天,也就是二周。具體情況如下所示... ...
  • 雖然使用不多,但是也有情況是需要在mysql 裡面寫語句開發功能的。在sql server 使用慣了,習慣了使用row_number() 函數進行排序,但是mysql 確沒有這樣一個函數。然後找到了po主寫的一篇 文章。通過變數賦值來查詢的。(PS 我測試的版本是mysql 5.6) 參考鏈接:ht ...
  • 簡介: Redis 是一個開源的,高性能的 key-value 系統,可以用來緩存或存儲數據。 Redis 數據可以持久化,並且支持多種數據類型:字元串(string),列表(list),哈希(hash),集合(set)和有序集合(sorted set)。 能夠對關係型資料庫起到很好的補充作用。它還 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...