SQL Server 和Azure SQL 索引架構和設計指南
文章推薦指數: 80 %
定義索引時,請考慮是否要以遞增或遞減順序儲存索引鍵資料行的資料。
遞增是預設值,而且會維持與舊版資料庫引擎的相容性。
CREATE INDEX、CREATE TABLE ...
跳到主要內容
已不再支援此瀏覽器。
請升級至MicrosoftEdge,以利用最新功能、安全性更新和技術支援。
下載MicrosoftEdge
其他資訊
目錄
結束焦點模式
閱讀英文
儲存
目錄
閱讀英文
儲存
編輯
Twitter
LinkedIn
Facebook
電子郵件
WeChat
目錄
SQLServer和AzureSQL索引架構和設計指南
發行項
03/17/2022
4位參與者
此頁面有所助益嗎?
Yes
No
還有其他意見反應嗎?
系統會將意見反應傳送給Microsoft:按下[提交]按鈕,您的意見反應將用來改善Microsoft產品和服務。
隱私權原則。
送出
謝謝。
本文內容
適用範圍:SQLServer(所有支援的版本)AzureSQLDatabaseAzureSQL受控執行個體AzureSynapseAnalytics是平行處理資料倉儲
設計不良的索引與不足的索引是資料庫應用程式瓶頸的主要原因。
設計有效的索引是達到良好資料庫和應用程式效能最重要的一點。
此索引設計指南包含索引架構的資訊,以及協助您設計符合應用程式需求的有效索引的最佳作法。
本指南假設讀者對可用的索引類型有大致的瞭解。
如需索引類型的一般描述,請參閱IndexTypes(索引類型)。
本指南涵蓋以下類型的索引:
主要儲存格式
索引類型
以磁片為基礎的rowstore
叢集
非叢集
唯一
Filtered
columnstore
叢集資料行存放區
非叢集資料行存放區
記憶體優化
雜湊
Memory-Optimized非叢集
如需XML索引的詳細資訊,請參閱Xml索引總覽和選擇性XML索引(SXI)。
如需空間索引的資訊,請參閱空間索引概觀。
如需全文檢索索引的資訊,請參閱擴展全文檢索索引。
索引設計基本概念
想像一下一般書籍:本書最後有一個索引,可協助您快速找出書籍內的資訊。
索引是排序的關鍵字清單,每個關鍵字旁會有頁碼,指出可以在哪些頁面找到這些關鍵字。
Rowstore索引並無不同:它是值的排序清單,而每個值都有指向這些值所在之資料頁面的指標。
索引本身會儲存在頁面上,稱為索引頁。
在一般書籍中,如果您要尋找所有包含"SQL"這個字的頁面指標,但索引橫跨多個頁面,您就必須翻頁直到您找到包含關鍵字"SQL"的索引頁面。
從該處,您可以遵循所有書籍頁面的指標。
如果您在索引的開頭建立一個頁面,包含可在哪裡找到每個字母的字母順序清單,就可以讓過程更有效率。
例如:「A到D-第121頁」、「E到G-第122頁」等。
這個額外的頁面,可以免除翻閱索引來找出開始位置的步驟。
這類頁面並不存在於一般書籍中,但存在於rowstore的索引中。
此單一頁面稱為索引的根頁面。
根頁面是索引所使用之樹狀結構的起始頁。
在樹狀結構中,包含實際資料指標的結束頁面稱為樹狀結構的「分葉頁面」。
索引是一種與資料表或檢視有關的磁碟內存或記憶體內部結構,它會加快從該資料表或檢視中擷取資料列的速度。
Rowstore索引包含從資料表或視圖中的一個或多個資料行建立的索引鍵。
針對rowstore索引,這些索引鍵會儲存在樹狀結構中(B+樹系),讓資料庫引擎可以快速且有效率地尋找與索引鍵值相關聯的資料列或資料列。
Rowstore索引會將資料以邏輯方式組織成具有資料列和資料行的資料表,並以資料列取向的資料格式實際儲存(稱為rowstore1),或儲存為數據行存放區的資料行取向資料格式。
為資料庫選擇正確的索引及工作負載時,往往很難在查詢速度與更新成本之間取得平衡。
以磁片為基礎的rowstore索引,或在索引鍵中有幾個資料行的索引,需要較少的磁碟空間和維護的額外負荷。
相反的,如果索引範圍較大,能涵蓋的查詢就更多。
在找到最有效率的索引之前,可能需要先試過數種不同的設計。
索引可以新增、修改和卸除,不會影響資料庫結構描述或應用程式的設計。
所以,不要吝於嘗試各種不同的索引。
在大部分的情況下,databaseengine的查詢最佳化工具會可靠地選擇最有效的索引。
整體的索引設計策略應該為查詢最佳化工具提供多樣化的索引,然後信任它會做最恰當的決定。
這可降低分析時間,且會在各種不同狀況下得到相當好的效能。
若要查看查詢最佳化工具用於特定查詢的索引,請在SQLServerManagementStudio的[查詢]功能表中,選取[包括實際執行計畫]。
使用索引不一定就會有良好的效能,良好的效能和有效率地使用索引也不能劃上等號。
如果使用索引對產生最佳效能一定有幫助,查詢最佳化工具的作業就很單純。
但事實上,選擇不正確的索引可能得不到最佳效能。
因此,查詢最佳化工具的工作是只有在提升效能時才選擇索引或索引組合,如果會妨礙效能,就要避免索引式擷取。
1資料列存放區一直是傳統儲存關聯式資料表資料的方式。
'Rowstore'是指基礎資料儲存格式為堆積、B+樹(叢集索引)或記憶體優化資料表的資料表。
「磁片型rowstore」會排除記憶體優化資料表。
索引設計工作
下列工作會構成我們建議的設計索引策略:
了解資料庫本身的特性。
例如,這是線上交易處理(OLTP)資料庫,而且經常進行資料修改,而且必須維持高輸送量?記憶體優化的資料表和索引特別適用于此案例,方法是提供無閂鎖的設計。
如需詳細資訊,請參閱本指南中的記憶體最佳化資料表的索引,或記憶體最佳化資料表設計指導的非叢集索引和記憶體最佳化資料表設計指導的雜湊索引。
或者,它是決策支援系統(DSS)或資料倉儲(OLAP)資料庫的範例,這些資料庫必須快速處理非常大型的資料集?資料行存放區索引特別適合一般資料倉儲資料集。
資料行存放區索引可以加快常用資料倉儲查詢(如篩選、彙總、群組及星型聯結查詢等)的速度,大幅改善使用者的資料倉儲經驗。
如需詳細資訊,請參閱本指南的資料行存放區索引概觀或資料行存放區索引設計指南。
了解最常使用的查詢特性。
例如,知道最常使用的查詢會聯結兩個以上的資料表,將有助於判斷要使用的最佳類型索引。
了解用於查詢的資料行特性。
例如,對於具有整數資料類型的資料行且也是唯一或非Null的資料行來說,索引是最理想的方式。
對於完善定義其資料子集的資料行,您可以在SQLServer2008與更新版本中使用篩選索引。
如需詳細資訊,請參閱本指南中的篩選索引設計指導方針。
建立或維護索引時,決定可能會提升效能的索引選項。
例如,ONLINE索引選項對於在現有的大型資料表上建立叢集索引就有幫助。
ONLINE選項會在建立或重建索引的同時,允許繼續進行基礎資料上的並行活動。
如需詳細資訊,請參閱設定索引選項。
決定最理想的索引儲存位置。
非叢集索引可以作為基礎資料表儲存在相同的檔案群組中,或儲存在不同的檔案群組中。
藉由增加磁碟I/O效能,索引的儲存位置可提升查詢效能。
例如,將非叢集索引儲存在不同磁碟機上(與資料表檔案群組不同的磁碟機)的檔案群組中,可以同時讀取多部磁碟機,所以可提升效能。
此外,叢集和非叢集索引可跨多個檔案群組使用資料分割結構描述。
當您考慮使用分割時,請決定是否應該校準索引,也就是說,使用分割資料表相同的方法進行分割,或獨立進行分割。
若要深入瞭解,請參閱本文的檔案群組或資料分割架構一節中的索引位置。
當您使用動態管理檢視來識別缺少的索引時(Dmv)例如sys.dm_db_missing_index_details和sys.dm_db_missing_index_columns,您可能會在相同的資料表和資料行(s上提供類似的索引變化。
檢查資料表上的現有索引以及遺漏索引建議,以避免建立重複的索引。
深入瞭解如何使用遺漏索引建議來微調非叢集索引。
一般索引設計指導方針
有經驗的資料庫管理員可以設計一組絕佳的索引,但這項工作很複雜、耗時且容易出錯,甚至是針對適度複雜的資料庫和工作負載。
了解資料庫、查詢和資料行的特性可以協助您設計最佳化的索引。
資料庫考量
當您設計索引時,請考慮下列資料庫指導方針:
資料表中的索引數量過多會影響到INSERT、UPDATE、DELETE和MERGE陳述式的效能,因為只要資料表中的資料一變更,所有的索引也都必須隨之適當調整。
例如,如果資料行會在數個索引中用到,而您執行了修改此資料行資料的UPDATE陳述式,則除了基礎基底資料表(堆積或叢集的索引)中的資料行之外,還必須更新每個含有該資料行的索引。
請避免對時常更新的資料表過度索引,保持索引窄小,愈少資料行愈好。
對不常更新、但有大量資料的資料表使用多個索引可增進查詢效能。
大量索引可以協助不修改資料之查詢的效能,例如SELECT陳述式,因為查詢最佳化工具有較多的索引可供選擇,以判斷最快的存取方法。
為小型資料表建立索引並不是最佳的方式,因為查詢最佳化工具透過查閱索引來搜尋資料,會比執行簡單的資料表掃描更費時。
因此,小型資料表上的索引不僅很少使用,而且還必須在資料表中的資料變更時進行維護。
當檢視包含彙總、資料表聯結或彙總與聯結的組合時,在檢視上建立索引可以提供重要的效能增進。
查詢中不必明確參考檢視,查詢最佳化工具會使用它。
AzureSQLDatabase中的主要複本上的資料庫會自動產生索引的Databaseadvisor效能建議。
您可以選擇性地啟用自動索引調整。
查詢存放區有助於找出具有optmial效能的查詢,並提供查詢執行計畫的歷程記錄,而這是由優化工具所選取的檔索引。
查詢考量
當您設計索引時,請考慮下列查詢指導方針:
在查詢之述詞及聯結條件經常使用的資料行上,建立非叢集索引。
這些是您的SARGable1資料行。
但應該避免加入不必要的資料行。
加入太多索引資料行可能會對磁碟空間和索引維護效能產生不利的影響。
涵蓋索引可以增進查詢效能,因為查詢就存在於索引本身裡面,所有需要的資料都符合查詢的需求。
也就是說,擷取要求的資料時只需要索引頁,非資料表或叢集索引的資料頁;因此,可以減少整體的磁碟I/O。
例如,資料行A的查詢,以及B在資料行AB上建立了複合索引的資料表,和C可以單獨從索引取出指定的資料。
重要
涵蓋索引是非叢集索引的指定,可在不存取其基底資料表和不產生查閱的情況下,直接解析一或數個類似的查詢結果。
這類索引在其分葉層級中擁有所有必要的非SARGable資料行。
這表示由SELECT子句或所有WHERE和JOIN引數傳回的資料行都在索引的涵蓋範圍內。
如果與資料表本身的資料列和資料行比較時,索引的範圍夠窄,那麼執行查詢時I/O可能就會減少許多,這表示它確實只佔資料行總數中的一小部分。
選取大型資料表的其中一小部分時,請考量涵蓋索引,以及固定述詞定義該小部分資料表的位置,例如只包含幾個非NULL值的稀鬆資料行。
撰寫的查詢應盡可能在一個陳述式中插入或修改最多資料列,而不是使用多個查詢來更新同樣的資料列。
只使用一個陳述式,才能利用到最佳化的索引維護方式。
評估查詢類型,以及查詢中如何使用資料行。
例如,在完全相符查詢類型中使用的資料行,就很適合當作非叢集或叢集索引。
1:關係資料庫中的「可優化」一詞是指可使用索引來加速查詢執行的S尋ARG>ument可以述詞。
資料行考慮
當您設計索引時,請考慮下列資料行指導方針:
讓叢集索引保持短小的索引鍵。
此外,對唯一或非Null資料行建立叢集索引,會有幫助。
ntext、text、image、varchar(max)、nvarchar(max)或varbinary(max)資料類型的資料行無法指定為索引鍵資料行。
但是,varchar(max)、nvarchar(max)、varbinary(max)和xml資料類型則可參與非叢集索引,作為非索引鍵之索引資料行。
如需詳細資訊,請參閱本指南中的內含資料行的索引一節。
xml資料類型只可以是XML索引的索引鍵資料行。
如需詳細資訊,請參閱XML索引。
SQLServer2012SP1引進了一種新類型的xml索引,稱為選擇性xml索引。
這個新索引可以改善以XML儲存之資料的查詢效能、讓大型XML資料工作負載的索引編制更快,並藉由減少索引本身的儲存成本來改善擴充性。
如需詳細資訊,請參閱選擇性XML索引(SXI)。
檢驗資料行唯一性。
在相同組合的資料行上,唯一索引可提供額外的資訊給查詢最佳化工具,讓索引變得更有用。
如需詳細資訊,請參閱本指南中的唯一索引設計指導方針。
檢驗資料行中的資料分散情形。
執行時間長的查詢往往是因為對唯一值少的資料行製作索引所造成,或對這樣的資料行執行聯結所造成。
這是資料與查詢本身的問題,若不找出問題,通常是無法解決的。
例如,如果城市裡每個人的名字都是Smith或Jones,那麼依姓氏排序的實體電話簿就無法使尋找某人的速度加快。
如需有關資料分散的詳細資訊,請參閱統計資料。
請考慮在具有定義良好之子集的資料行(如疏鬆資料行)、大部分的值都是NULL的資料行、具有值類別的資料行以及具有相異值範圍的資料行上使用篩選的索引。
設計良好的已篩選索引可以提升查詢效能、降低索引維護成本,並減少儲存成本。
如果索引將包含多個資料行,可考慮資料行的順序。
用於WHERE子句等於(=)、大於(>)、小於(、>=、1的資料列。
根據預設,非叢集索引只有一個資料分割。
當非叢集索引有多個資料分割時,每個資料分割都有B+樹狀結構,其中包含該特定資料分割的索引資料列。
例如,如果非叢集索引有四個數據分割,則有四個B+樹狀結構,每個分割區中有一個。
視非叢集索引中的資料類型而定,每個非叢集索引結構會有一個或多個配置單位來儲存並管理特定資料分割的資料。
每個非叢集索引至少會有一個儲存索引B+樹系頁面的每個分割區IN_ROW_DATA配置單位。
如果非叢集索引包含大型物件(LOB)資料行,則在該非叢集索引中,每個資料分割也會有一個LOB_DATA配置單位。
此外,如果資料分割包含超過8060位元組資料列大小限制的可變長度資料行,則每個分割區都會有一個ROW_OVERFLOW_DATA配置單位。
下圖顯示單一資料分割中非叢集索引的結構。
資料庫考量
設計非叢集索引時,請考慮資料庫的特性。
更新需求較低但資料容量較大的資料庫或資料表,可以從許多非叢集索引中得到好處以增進查詢效能。
請考慮針對定義完善的資料子集建立篩選的索引來提升查詢效能、降低索引儲存成本,並降低與完整資料表非叢集索引比較的索引維護成本。
包含主要唯讀資料的決策支援系統(DSS)應用程式和資料庫,可以從許多非叢集索引中得到好處。
查詢最佳化工具有較多可供選擇的索引,因此可判斷最快的存取方法,而資料庫的低更新特性則表示索引維護不會降低效能。
包含大量已更新資料表的線上交易處理(OLTP)應用程式和資料庫應避免重複進行索引作業。
此外,應該縮小索引的範圍,這表示應盡可能包含較少的資料行。
資料表中的索引數量過多會影響到INSERT、UPDATE、DELETE和MERGE陳述式的效能,因為只要資料表中的資料一變更,所有的索引也都必須隨著調整。
查詢考量
建立非叢集索引之前,應該先了解資料的存取方式。
請考慮針對具有下列屬性的查詢使用非叢集索引:
使用JOIN或GROUPBY子句。
依據聯結和群組操作中用到的資料行,建立多個非叢集索引,並依據外部索引鍵資料行建立一個叢集索引。
不會傳回大量結果集的查詢。
建立篩選的索引,以涵蓋從大型資料表傳回定義完善之資料列子集的查詢。
提示
CREATEINDEX陳述式的WHERE子句通常會與所涵蓋查詢的WHERE子句相符。
包含會傳回完全相符項目的查詢(例如WHERE子句)中,所設定的搜尋條件常會用到的資料行。
提示
新增新的索引時,請考量成本與好處。
最好是將其他查詢需求合併到現有的索引中。
例如,如果允許涵蓋數個重要查詢,即可考量在現有索引中增加一或兩個額外的分葉層級資料行,而不是每個重要查詢都有一個完全涵蓋索引。
資料行考慮
考慮具有一或多個下列屬性的資料行:
涵蓋查詢。
當索引包含查詢中的所有資料行時,將可改善效能。
查詢最佳化工具可以在索引中尋找所有資料行值;未被存取的資料表或叢集索引資料會導致磁碟I/O作業減少。
使用內含資料行索引來新增涵蓋的資料行,而非建立廣泛的索引鍵。
如果資料表有叢集索引,則會自動將叢集索引中定義的資料行加入至資料表上的每個非叢集索引。
這樣會產生涵蓋的查詢,而不需要在非叢集索引的定義中指定叢集索引資料行。
例如,如果資料表在資料行C上有叢集索引,則在資料行B上有非唯一的非叢集索引,而且A將擁有其索引鍵值資料行B、A和C。
深入瞭解非叢集索引架構。
許多不同值的資料行,例如姓氏與名字的組合(如果其他資料行使用叢集索引的話)。
如果只有很少量的不同值(例如只有1和0),則大多數的查詢都不會使用索引,因為資料表掃描通常會更有效率。
如果是這種資料類型,請考慮針對只發生於少量資料列的相異值建立篩選的索引。
例如,如果大多數的值為0,則查詢最佳化工具可能會將篩選的索引用於包含1的資料列。
使用內含資料行擴充非叢集索引
您可以加入非索引鍵資料行至非叢集索引的分葉層級,以擴充非叢集索引的功能。
藉由加入非索引鍵資料行,您可以建立涵蓋更多查詢的非叢集索引。
這是因為非索引鍵之索引資料行有下列好處:
與索引鍵資料行一樣,它們可以是不允許的資料類型。
在計算索引鍵資料行的數目或索引鍵大小時,資料庫引擎不會考慮它們。
查詢中所有的資料行在索引中當做索引鍵或非索引鍵之索引資料行時,內含非索引鍵之索引資料行索引可以大幅改進查詢效能。
因為查詢最佳化工具可以在索引中找到所有資料行值,所以可以提高效能;不存取資料表或叢集索引資料,導致磁碟I/O作業變少。
注意
索引包含查詢參考的所有資料行時,通常就是指涵蓋查詢。
索引鍵資料行儲存在索引的所有分葉層級上,而非索引鍵之索引資料行僅儲存在分葉層級上。
使用內含資料行避免大小限制
您可以在非叢集索引中包含非索引鍵之索引資料行,以避免超出目前索引大小限制(最大16個索引鍵資料行,最大900個位元組索引鍵大小)計算索引鍵資料行數或索引鍵大小時,DatabaseEngine不會考慮非索引鍵之索引資料行。
例如,假設您想要在Document資料表中建立下列資料行的索引:
Titlenvarchar(50)
Revisionnchar(5)
FileNamenvarchar(400)
因為Nchar和Nvarchar資料類型的每個字元都需要2個位元組,所以包含這三個數據行的索引會超過900個位元組的大小限制,(455*2)。
使用INCLUDE陳述式的CREATEINDEX子句,索引鍵可定義為(Title,Revision),而FileName則定義為非索引鍵之索引資料行。
這樣,索引鍵大小會是110個位元組(55*2),且索引仍能包含所有必須的資料行。
下列陳述式會建立這類索引。
CREATEINDEXIX_Document_Title
ONProduction.Document(Title,Revision)
INCLUDE(FileName);
GO
如果您正遵循程式碼範例,您可以使用這個SQLtransact-sql語句來卸載此索引:
DROPINDEXIX_Document_Title
ONProduction.Document;
GO
具有內含資料行的索引方針
設計具有內含資料行的非叢集索引時,請考慮下列指導方針:
非索引鍵之索引資料行是定義於CREATEINDEX陳述式的INCLUDE子句。
非索引鍵之索引資料行僅能定義於資料表或索引檢視的非叢集索引上。
允許所有的資料類型,除了text、ntext和image以外。
具決定性之精確或非精確的計算資料行都可以當做內含資料行。
如需詳細資訊,請參閱計算資料行的索引。
如同索引鍵資料行,只要計算資料行資料類型可以作為無索引鍵索引資料行,則從image、ntext與text衍生的計算資料行即可以是非索引鍵之索引資料行。
資料行名稱無法同時指定於INCLUDE清單與索引鍵資料行清單兩者中。
資料行名稱在INCLUDE清單中不得重複。
資料行大小指導方針
至少必須定義一個索引鍵資料行。
非索引鍵之索引資料行數目的上限為1023個資料行。
這是資料表資料行數目的上限減1。
索引鍵資料行(不包含非索引鍵之索引資料行)必須遵守現有索引大小的限制(上限為16個索引鍵資料行),且索引鍵總大小為900個位元組。
所有非索引鍵之索引資料行大小總計僅由INCLUDE子句中指定的資料行大小限定;例如,varchar(max)資料行是限定為2GB。
資料行修改指導方針
當您修改定義為內含資料行的資料表資料行時,則下列限制適用:
必須先卸除索引,才能從資料表卸除非索引鍵之索引資料行。
除非執行下列動作,否則無法變更非索引鍵之索引資料行:
將資料行的Null屬性從NOTNULL變更為NULL。
增加varchar、nvarchar或varbinary資料行的長度。
注意
這些資料行修改限制也適用索引鍵資料行。
設計建議
重新設計具有大型索引鍵大小的非叢集索引,如此僅有用於搜尋與查閱的資料行才會是索引鍵資料行。
讓涵蓋查詢的所有其他資料行都作為內含非索引鍵之索引資料行。
如此一來,您將擁有涵蓋查詢所需的所有資料行,但是索引鍵本身會變得很小而且很有效率。
例如,假設您要設計能夠涵蓋下列查詢的索引。
SELECTAddressLine1,AddressLine2,City,StateProvinceID,PostalCode
FROMPerson.Address
WHEREPostalCodeBETWEENN'98000'ANDN'99999';
GO
若要涵蓋查詢,必須在索引中定義每個資料行。
雖然您可以將所有資料行定義為索引鍵資料行,但是索引鍵大小應是334個位元組。
由於只有實際作為搜尋條件的資料行才是PostalCode資料行,且長度為30個位元組,所以較佳的索引設計方式應該是將PostalCode定義為索引鍵資料行,並將所有其他的資料行作為非索引鍵之索引資料行包含在內。
下列陳述式會建立具有內含資料行的索引,可以涵蓋查詢。
CREATEINDEXIX_Address_PostalCode
ONPerson.Address(PostalCode)
INCLUDE(AddressLine1,AddressLine2,City,StateProvinceID);
若要驗證索引是否涵蓋查詢,請建立索引,然後顯示預估執行計畫。
如果執行計畫只顯示索引的SELECT運算子和IndexSeek運算子IX_Address_PostalCode,則查詢已由索引「涵蓋」。
您可以使用下列語句來卸載索引:
DROPINDEXIX_Address_PostalCode
ONPerson.Address;
GO
效能考量
避免加入不必要的資料行。
加入過多的索引資料行、索引鍵或無索引鍵,可能會發生以下的效能問題:
頁面上可以放入的索引資料列變少。
這將使得I/O的作業增加而降低快取的效率。
必須有更多磁碟空間才能儲存索引。
尤其是,新增varchar(max)、nvarchar(max)、varbinary(max)或xml資料類型作為非索引鍵之索引資料行,將大幅增加磁碟空間的需求。
這是因為資料行的值複製到索引的分葉層級。
因此,它們會同時存在於索引與基底資料表中。
維護索引時,會增加修改、插入、更新或刪除基礎資料表或索引檢視的時間。
您必須判斷在資料修改期間以及額外的磁碟空間需求中,查詢效能的提升是否超過效能的影響。
唯一索引設計指導方針
唯一索引可保證索引鍵不包含重複值,因此資料表中的每一個資料列在某方面來說是唯一的。
只有當資料具有唯一的特性時,指定唯一索引才有意義。
例如,若要確定NationalIDNumber資料表中HumanResources.Employee資料行的值是唯一的,則當主索引鍵是EmployeeID時,請在NationalIDNumber資料行上建立UNIQUE條件約束。
如果使用者試著在該資料行上為不止一位員工輸入相同值,便會顯示錯誤訊息,而且無法輸入重複值。
利用多重資料行唯一索引,此索引可保證索引鍵的每一個值組合都是唯一的。
例如,若在LastName、FirstName和MiddleName資料行的組合上建立唯一索引,則該資料表中不得有兩個資料列具有這些資料行的相同值組合。
叢集與非叢集索引都可以是唯一的。
如果資料行中的資料是唯一的,您可以在同一個資料表上建立唯一的叢集索引和多個唯一的非叢集索引。
唯一索引的好處包括:
確保所定義之資料行的資料完整性。
提供對查詢最佳化工具有幫助的其他資訊。
建立PRIMARYKEY或UNIQUE條件約束時,會自動在指定的資料行上建立唯一索引。
建立UNIQUE條件約束和不使用條件約束而建立唯一索引,兩者之間並沒有顯著的差異。
資料驗證的方式相同,且查詢最佳化工具不會區分唯一索引是由條件約束所建立還是手動建立的。
不過,當您的目標是資料完整性時,就應該在資料行上建立UNIQUE或PRIMARYKEY條件約束。
如此一來,索引的目標就很明確。
考量
如果資料中已存在重複的索引鍵值,則無法建立唯一索引、UNIQUE條件約束或PRIMARYKEY條件約束。
如果資料是唯一的,而且您想要強制唯一性,則在相同資料行組合上建立唯一索引而不是非唯一索引,可提供其他資訊給查詢最佳化工具,來產生更有效率的執行計畫。
在這種情況下,建議您建立唯一索引(而且最好先經由建立UNIQUE條件約束)。
唯一非叢集索引可有內含的非索引鍵之索引資料行。
如需詳細資訊,請參閱內含資料行的索引。
篩選索引設計指導方針
篩選索引是最佳化的非叢集索引,特別適合用來處理會從定義完善之資料子集進行選取的查詢。
篩選索引會使用篩選述詞對資料表中的部分資料列進行索引。
與完整資料表索引相較,設計良好的篩選索引可以提升查詢效能、降低索引維護成本,並降低索引儲存成本。
篩選索引可以提供全資料表索引所不及的下列優勢:
提升的查詢效能和計畫品質
設計良好的篩選索引可以提升查詢效能和執行計畫品質,因為它比全資料表的非叢集索引來得小,且具有篩選統計資料。
篩選統計資料比全資料表統計資料更為正確,因為僅涵蓋篩選索引中的資料列。
降低的索引維護成本
只有在資料操作語言(DML)陳述式影響到索引中的資料時,才會對索引進行維護。
與全資料表的非叢集索引相較,篩選索引可以降低維護成本,因為後者較小且僅會在索引中的資料受到影響時才會進行維護。
篩選索引的數量可能很多,特別是當其包含不常受到影響的資料時。
同樣地,如果篩選索引僅包含經常受到影響的資料,則因為索引的大小較小,更新統計資料的成本就會下降。
降低的索引儲存成本
在不需要全資料表索引時,建立篩選索引可以縮減非叢集索引的磁碟儲存量。
您可以使用多個篩選索引來取代全資料表的非叢集索引,而不會大幅增加儲存需求。
當資料行包含定義良好的資料子集且查詢在SELECT陳述式中會參考這些資料時,篩選索引很有用。
範例包括:
僅包含一些非NULL值的疏鬆資料行。
包含資料類別的異質資料行。
包含金額、時間和日期等值範圍的資料行。
由資料行值的簡單比較邏輯所定義的資料表資料分割。
當索引中的資料列數與全資料表索引相較比較小時,篩選索引降低的維護成本最明顯。
如果篩選索引包含資料表中大部分的資料列,則其維護成本可能要比全資料表索引高。
在此種情況下應該使用全資料表索引,而不是篩選索引。
篩選索引定義於單一資料表,僅支援簡單比較運算子。
如果需要參考多個資料表或具有複雜邏輯的篩選運算式,則應該建立檢視。
設計考量
為了設計有效的篩選索引,必須了解應用程式所使用的查詢以及這些查詢與資料子集的關聯。
具有定義良好之子集的資料範例包括大部分的值都是NULL的資料行、具有異質值類別的資料行,以及具有相異值範圍的資料行。
下列的設計考量提供多種案例,說明何時篩選索引的優點多於全資料表索引。
提示
非叢集資料行存放區索引定義支援使用篩選的條件。
若要將OLTP資料表新增資料行存放區索引對效能的影響降到最低,請只對您作業的工作負載冷資料,使用篩選的條件建立非叢集資料行存放區索引。
資料子集的篩選索引
當資料行僅具有少數的查詢相關值時,可以在值的子集上建立篩選索引。
例如,當資料行中的值大部分都是NULL且查詢只會從非NULL值進行選取時,您可以針對非NULL的資料列建立篩選索引。
所產生的索引比在相同的索引鍵資料行上定義的全資料表非叢集索引還小,維護成本也比較低。
例如,AdventureWorks範例資料庫有一個Production.BillOfMaterials具有2679資料列的資料表。
EndDate資料行只有199個包含非NULL值的資料列,其他的2480個資料列都是包含NULL。
下列篩選索引所包含的查詢會傳回在索引中定義的資料行,並僅會選取EndDate使用非NULL值的資料列。
CREATENONCLUSTEREDINDEXFIBillOfMaterialsWithEndDate
ONProduction.BillOfMaterials(ComponentID,StartDate)
WHEREEndDateISNOTNULL;
GO
篩選索引FIBillOfMaterialsWithEndDate對下列查詢有效。
顯示估計的執行計畫,以判斷查詢最佳化工具是否使用篩選的索引。
SELECTProductAssemblyID,ComponentID,StartDate
FROMProduction.BillOfMaterials
WHEREEndDateISNOTNULL
ANDComponentID=5
ANDStartDate>'20080101';
GO
如需有關如何建立篩選索引以及如何定義篩選索引述詞運算式的詳細資訊,請參閱建立篩選的索引。
異質資料的篩選索引
當資料表具有異質資料列時,您可以針對一或多個資料類別建立篩選索引。
例如,列於Production.Product資料表中的每個產品都會被指派ProductSubcategoryID,此ID又會與產品類別Bikes、Components、Clothing或Accessories等產生關聯。
這些類別都是異質性的,因為它們在Production.Product資料表中的資料行值並沒有緊密關聯。
例如,對個別產品類別來說,Color、ReorderPoint、ListPrice、Weight、Class和Style等資料行都有其唯一的特性。
假設經常對具有介於27到36(含)之子類別的配件進行查詢。
您只要在配件子類別上建立篩選索引,就可以改善查詢該配件的效能,如下列範例所示。
CREATENONCLUSTEREDINDEXFIProductAccessories
ONProduction.Product(ProductSubcategoryID,ListPrice)
Include(Name)
WHEREProductSubcategoryID>=27ANDProductSubcategoryID<=36;
GO
篩選的索引FIProductAccessories涵蓋下列查詢,因為查詢結果會包含在索引中,而且查詢計劃不包含基表查閱。
例如,查詢述詞運算式ProductSubcategoryID=33是篩選之索引述詞ProductSubcategoryID>=27的子集,而且查詢述詞中的ProductSubcategoryID<=36、ProductSubcategoryID與ListPrice資料行都是索引中的索引鍵資料行,因此會將名稱當做包含的資料行,儲存在索引的分葉層級中。
SELECTName,ProductSubcategoryID,ListPrice
FROMProduction.Product
WHEREProductSubcategoryID=33ANDListPrice>25.00;
GO
索引鍵資料行
最佳作法是在篩選索引定義中包含少數的索引鍵或內含資料行,並僅併入查詢最佳化工具在選擇查詢執行計畫的篩選索引時必要的資料行。
查詢最佳化工具可以選擇查詢的篩選索引,不論它是否涵蓋該查詢。
然而,查詢最佳化工具如果涵蓋該查詢,則更可能選擇篩選索引。
在某些情況下,篩選索引會涵蓋查詢,而不需將資料行以篩選索引定義中的索引鍵或內含資料行方式包含在篩選索引運算式中。
下列指導方針說明篩選索引運算式中的資料行何時應該是篩選索引定義中的索引鍵或內含資料行。
其中的範例會參考先前所建立的篩選索引FIBillOfMaterialsWithEndDate。
如果篩選索引運算式相等於查詢述詞,且查詢並未以篩選索引運算式中的資料行傳回查詢結果,則篩選索引運算式中的資料行不需要是篩選索引定義中的索引鍵或內含資料行。
例如,FIBillOfMaterialsWithEndDate包含下列查詢,因為查詢述詞相當於篩選運算式,且查詢結果未傳回EndDate。
FIBillOfMaterialsWithEndDate不需要在篩選的索引定義中以EndDate作為索引鍵或內含資料行。
SELECTComponentID,StartDateFROMProduction.BillOfMaterials
WHEREEndDateISNOTNULL;
如果查詢述詞在不等於篩選索引運算式的比較中使用篩選索引運算式中的資料行,則該資料行應該是篩選索引定義中的索引鍵或內含資料行。
例如,FIBillOfMaterialsWithEndDate對下列查詢有效,因為它會從篩選索引選取資料列子集。
不過,它並不會涵蓋下列查詢,因為EndDate會用於比較EndDate>'20040101'中,而後者並不相等於篩選索引運算式。
在未先查閱EndDate的值之前,查詢處理器無法執行此查詢。
因此,EndDate應該是篩選索引定義中的索引鍵或內含資料行。
SELECTComponentID,StartDateFROMProduction.BillOfMaterials
WHEREEndDate>'20040101';
如果篩選索引運算式中的資料行在查詢結果集中,則該資料行應該是篩選索引定義中的索引鍵或內含資料行。
例如,FIBillOfMaterialsWithEndDate並不涵蓋下列查詢,因為它會在查詢結果中傳回EndDate資料行。
因此,EndDate應該是篩選索引定義中的索引鍵或內含資料行。
SELECTComponentID,StartDate,EndDateFROMProduction.BillOfMaterials
WHEREEndDateISNOTNULL;
資料表的叢集索引鍵並不需要是篩選索引定義中的索引鍵或內含資料行。
叢集索引鍵會自動包含在所有非叢集的索引中(包含篩選索引在內)。
FIBillOfMaterialsWithEndDate若要卸載和FIProductAccessories索引,請執行下列語句:
DROPINDEXFIBillOfMaterialsWithEndDate
ONProduction.BillOfMaterials;
GO
DROPINDEXFIProductAccessories
ONProduction.Product;
GO
篩選述詞中的資料轉換運算子
如果在篩選索引的篩選索引運算式中指定的比較運算子產生隱含或明確的資料轉換,則如果該轉換是發生在比較運算子的左側,就會發生錯誤。
解決方案是以資料轉換運算子(CAST或CONVERT)在比較運算子的右側寫下篩選索引運算式。
下列範例會建立具有多種資料類型的資料表。
CREATETABLEdbo.TestTable(aint,bvarbinary(4));
GO
在下列篩選索引定義中,資料行b會隱含地轉換成整數資料類型,以與常數1進行比較。
這會產生錯誤訊息10611,因為轉換會發生在篩選述詞中運算子的左邊。
CREATENONCLUSTEREDINDEXTestTabIndexONdbo.TestTable(a,b)
WHEREb=1;
GO
解決方法是將右手邊的常數轉換成與資料行b相同的類型,如下列範例所示:
CREATEINDEXTestTabIndexONdbo.TestTable(a,b)
WHEREb=CONVERT(Varbinary(4),1);
GO
將資料轉換從比較運算子的左側移至右側可能會變更轉換的意義。
在上述範例中,當CONVERT運算子新增至右側時,比較作業會從整數比較變更為varbinary比較。
藉由執行下列語句,卸載在此範例中建立的物件:
DROPTABLETestTable;
GO
資料行存放區索引設計指導方針
資料行存放區索引是一種技術,可使用單欄式資料格式(稱為資料行存放區)來儲存、取出和管理資料。
如需詳細資訊,請參閱資料行存放區索引總覽。
如需版本資訊,請參閱資料行存放區索引-新增功能。
資料行存放區索引架構
了解這些基本知識,可讓您更容易理解其他說明有效使用方式的資料行存放區文章。
使用資料行存放區和資料列存放區壓縮的資料儲存
討論資料行存放區索引時,我們使用「資料列存放區」和「資料行存放區」等字詞強調資料儲存格式。
資料行存放區索引會使用這兩種儲存類型。
「資料行存放區」是以邏輯方式組織成資料表的資料,其中包含資料列和資料行,並且會以資料行取向的資料格式實際儲存。
資料行存放區索引是以資料行存放區格式實際儲存大部分的資料。
在資料行存放區格式中,資料會以資料行的方式進行壓縮和解壓縮。
這樣就不需要解壓縮每個資料列中查詢未要求的其他值。
如此一來,便可快速掃描大型資料表的整個資料行。
「資料列存放區」是以邏輯方式組織成資料表的資料,其中包含資料列和資料行,並且會以資料列取向的資料格式實際儲存。
這是儲存關聯式資料表資料的傳統方式,例如堆積或叢集B+樹狀結構索引。
資料行存放區索引也會以資料列存放區格式實際儲存某些資料列,其稱為差異存放區。
差異存放區(也稱為差異資料列群組)是一種保存空間,用來保存數量太少而沒有資格壓縮到資料行存放區的資料列。
每個差異資料列群組都會實作為叢集B+樹狀結構索引。
差異存放區是一種保存空間,用來保存數量太少而無法壓縮到資料行存放區的資料列。
差異存放區會以資料列存放區格式儲存資料列。
如需資料行存放區詞彙與概念的詳細資訊,請參閱資料行存放區索引:概觀。
作業是在資料列群組和資料行區段上執行
資料行存放區索引會將資料列分組成可管理的單位。
每個單位稱為資料列群組。
為了達到最佳效能,資料列群組中的資料列數量必須多到足以改善壓縮率,並且少到足以獲益於記憶體內部作業。
例如,資料行存放區索引會對資料列群組執行下列作業:
將資料列群組壓縮到資料行存放區。
對資料列群組內的每個資料行區段執行壓縮。
在ALTERINDEX...REORGANIZE作業期間合併資料列群組,包括移除已刪除的資料。
在ALTERINDEX...REBUILD作業期間建立新的資料列群組。
在動態管理檢視(DMV)中報告資料列群組健全狀況和片段。
差異存放區是由一或多個資料列群組所組成,稱為差異資料列群組。
每個差異資料列群組都是叢集B+樹狀結構索引,用來儲存小規模的大量載入和插入,直到資料列群組包含1048576個數據列為止,此時呼叫元組的進程會自動將關閉的資料列群組壓縮到資料行存放區。
如需有關資料列群組狀態的詳細資訊,請參閱sys.dm_db_column_store_row_group_physical_stats(Transact-SQL)。
提示
太多小型資料列群組會降低資料行存放區索引的品質。
遵循內部閾值原則(決定如何移除已刪除的資料列以及合併已壓縮的資料列群組),重新組織作業將合併較小的資料列群組。
合併之後,索引品質應該會改善。
注意
從SQLServer2019(15.x)開始,TupleMover會由背景合併工作協助,該工作會自動壓縮已存在一段時間的較小OPEN差異資料列群組(由內部閾值決定),或合併已刪除大量資料列的COMPRESSED資料列群組。
在每個資料列群組中,每個資料行都有一些資料行值。
這些值稱為資料行區段。
每一個資料列群組會針對資料表中的每一個資料行包含一個資料行區段。
在每個資料列群組中,每個資料行都有一個資料行區段。
當資料行存放區索引壓縮資料列群組時,它會個別壓縮每一個資料行區段。
若要解壓縮整個資料行,資料行存放區索引只需要解壓縮每個資料列群組中的一個資料行區段。
如需資料行存放區詞彙與概念的詳細資訊,請參閱資料行存放區索引:概觀。
少量載入和插入會進入差異存放區
藉由一次至少將102,400個資料列壓縮到資料行存放區索引,資料行存放區索引可改善資料行存放區壓縮和效能。
為了大量壓縮資料列,資料行存放區索引會在差異存放區中累積少量載入和插入。
差異存放區作業將由幕後處理。
為了能傳回正確的查詢結果,叢集資料行存放區索引會結合資料行存放區和差異存放區兩方面的查詢結果。
資料列會進入差異存放區的情況:
和INSERTINTO...VALUES陳述式一起插入。
位於大量載入結尾,而且數量小於102,400。
已更新。
每項更新的實作方式為刪除和插入。
差異存放區也會儲存已刪除資料列的識別碼清單,已刪除資料列是標示為已刪除,但尚未從資料行存放區中實際刪除的資料列。
如需資料行存放區詞彙與概念的詳細資訊,請參閱資料行存放區索引:概觀。
當delta資料列群組已滿時,會壓縮成資料行存放區
叢集資料行存放區索引在將資料列群組壓縮到資料行存放區之前,在每個差異資料列群組中最多可收集1,048,576個資料列。
如此可改善資料行存放區索引的壓縮。
差異資料列群組一旦達到資料列數目上限,就會從OPEN轉換為CLOSED狀態。
名為TupleMover的背景處理序會檢查已關閉的資料列群組。
如果處理序發現已關閉的資料列群組,便會壓縮資料列群組,並將其儲存至資料行存放區中。
當差異資料列群組已壓縮時,現有的差異資料列群組會轉換成TOMBSTONE狀態,供TupleMover稍後在其沒有參考時移除,且新的已壓縮資料列群組會標記為COMPRESSED。
如需有關資料列群組狀態的詳細資訊,請參閱sys.dm_db_column_store_row_group_physical_stats(Transact-SQL)。
您可以使用ALTERINDEX強制將差異資料列群組壓縮到資料行存放區,以重建或重新組織索引。
請注意,如果在壓縮期間有記憶體不足的壓力,資料行存放區索引可能會減少已壓縮資料列群組中的資料列數量。
如需資料行存放區詞彙與概念的詳細資訊,請參閱資料行存放區索引:概觀。
每個資料表資料分割都有自己的資料列群組和差異資料列群組
叢集索引、堆積和資料行存放區索引中的資料分割概念都相同。
分割資料表作業會根據一系列的資料行值,將資料表分割成較小的資料列群組。
這通常用於管理資料。
例如,您可以針對每一年的資料建立一個資料分割,然後使用資料分割切換將封存資料移至較便宜的儲存體。
資料分割切換適用於資料行存放區,可輕鬆地將資料的資料分割移至另一個位置。
資料列群組一律定義在資料表資料分割內。
當分割資料行存放區索引時,每個資料分割都有自己的壓縮資料列群組和差異資料列群組。
提示
如果需要從資料行存放區移除資料,請考慮使用資料表分割。
退出並截斷不再需要的資料分割是一個有效率的策略,可刪除資料,而不會產生較小的資料列群組所引進的片段。
每個資料分割可以有多個差異資料列群組
每個資料分割可以有多個差異資料列群組。
當資料行存放區索引需要在差異資料列群組中新增資料,而差異資料列群組已鎖定時,資料行存放區索引會嘗試取得不同差異資料列群組的鎖定。
如果沒有任何可用的差異資料列群組,資料行存放區索引就會建立新的差異資料列群組。
例如,具有10個資料分割的資料表可以輕鬆擁有20多個差異資料列群組。
您可以合併相同資料表上的資料行存放區索引和資料列存放區索引
非叢集索引包含基礎資料表中部分或所有資料列和資料行的複本。
此索引會定義為資料表的一或多個資料行,並具有篩選資料列的選用條件。
您可以在rowstore資料表上建立可更新的非叢集資料行存放區索引。
資料行存放區索引會儲存資料的複本,因此您需要額外的儲存空間。
不過,資料行存放區索引中資料的壓縮大小比資料列存放區資料表所需大小還要小。
如此一來,您就可以同時在資料行存放區索引上執行分析,並在資料列存放區索引上執行交易。
當資料列存放區資料表中的資料變更時,會更新資料行存放區,讓兩個索引會針對相同的資料執行。
您可以在資料行存放區索引上有一或多個非叢集rowstore索引。
如此一來,您就可以對基礎資料行存放區執行有效率的資料表搜尋。
其他選項現在也可以使用。
例如,您可以在資料列存放區資料表上使用UNIQUE條件約束,強制執行主索引鍵條件約束。
由於非唯一的值無法插入rowstore資料表中,因此資料庫引擎無法將值插入資料行存放區中。
效能考量
非叢集資料行存放區索引定義支援使用篩選的條件。
若要將OLTP資料表新增資料行存放區索引對效能的影響降到最低,請只對您作業的工作負載冷資料,使用篩選的條件建立非叢集資料行存放區索引。
記憶體中的資料表可以有一個資料行存放區索引。
您可以在建立資料表時予以建立,或稍後使用ALTERTABLE(Transact-SQL)將其加入。
在SQLServer2016(13.x)之前,只有磁碟資料表可以有資料行存放區索引。
如需詳細資訊,請參閱資料行存放區索引-查詢效能。
設計指引
資料列存放區資料表可以有一個可更新的非叢集資料行存放區索引。
在SQLServer2014(12.x)之前,非叢集資料行存放區索引是唯讀的。
如需詳細資訊,請參閱資料行存放區索引-設計指引。
雜湊索引設計指導方針
所有記憶體最佳化資料表都必須至少有一個索引,因為它是將資料列連線在一起的索引。
在記憶體最佳化資料表上,每個索引也會進行記憶體最佳化。
雜湊索引是記憶體最佳化資料表中可能有的索引類型之一。
如需詳細資訊,請參閱記憶體最佳化資料表的索引。
適用範圍:SQLServer(所有支援的版本)AzureSQLDatabaseAzureSQL受控執行個體
雜湊索引架構
雜湊索引由指標陣列組成,而陣列的每個項目稱為雜湊值區。
每個值區是8位元組,可用來儲存索引鍵項目連結清單的記憶體位址。
每個項目都是一個索引鍵的值,加上其基礎記憶體最佳化資料表中對應資料列的位址。
每個項目指向項目連結清單中的下一個項目,全都連結到目前的值區。
定義索引時必須指定值區的數目:
值區與資料表資料列或相異值的比率越低,平均值區連結清單就越長。
短連結清單的執行速度比長連結清單還快。
雜湊索引的值區數目上限是1,073,741,824。
提示
若要判斷您資料的正確BUCKET_COUNT,請參閱設定雜湊索引值區計數。
索引鍵資料行要套用雜湊函式,且函式的結果會決定索引鍵落入哪些值區。
每個值區都有一個資料列指標,這些資料列的雜湊索引鍵值會對應到該值區。
用於雜湊索引的雜湊函數具有下列特性:
資料庫引擎有一個雜湊函數可用於所有雜湊索引。
該雜湊函數具決定性。
相同的輸入索引鍵值一律對應到雜湊索引中的相同值區。
多個索引鍵可能對應至相同的雜湊值區。
平衡雜湊函數,表示索引鍵值在雜湊值區上的分配通常會遵循波氏或常態分佈,不是線性分佈。
波氏分配不是平均分配。
索引鍵值不會平均分佈在雜湊值區中。
如果兩個索引鍵對應到相同雜湊值區,就會發生雜湊衝突。
大量的雜湊衝突可能會對讀取作業產生效能影響。
實際目標是包含兩個不同索引鍵值之值區的30%。
下圖摘要說明雜湊索引和值區的相互作用。
設定雜湊索引值區計數
雜湊索引值區計數是在索引建立時間指定,並可使用ALTERTABLE...ALTERINDEXREBUILD語法來變更。
在大部分情況下,值區計數理想情況會介於索引鍵中相異值數目的1到2倍之間。
您不一定能夠預測某個特定索引鍵可能擁有或將會擁有多少個值。
如果BUCKET_COUNT值在實際索引鍵值數目的10倍內,效能通常仍然不錯,高估一般而言會比低估好。
值區太「少」會有下列缺點︰
有更多相異索引鍵值的雜湊衝突。
每個相異值都會強制與不同的相異值共用相同的值區。
每個值區的平均鏈結長度都會增加。
值區鏈結越長,索引中的等號比較查閱的速度就越慢。
值區太「多」會有下列缺點︰
值區計數過高可能會導致更多的空值區。
空值區會影響完整索引掃描的效能。
如果是定期執行,請考慮挑選接近相異索引鍵值數目的值區計數。
空值區會使用記憶體,但每個值區只使用8個位元組。
注意
新增更多值區,對於減少將共用重複值的項目鏈結在一起的情況,沒有任何助益。
您可以使用值重複的比率來決定雜湊是否為適當的索引類型,而不是計算值區計數。
效能考量
雜湊索引的效能如下︰
當WHERE子句的述詞為雜湊索引鍵中的每個資料行指定確切的值時極佳。
若是不相等的述詞,雜湊索引會還原至掃描。
當WHERE子句的述詞在索引鍵中尋找某個值範圍時效能不佳。
當WHERE子句的述詞為有兩個資料行的雜湊索引鍵的第一個資料行指定一個特定值,但未針對該索引鍵的其他資料行指定值時,效能不佳。
提示
述詞必須包含雜湊索引鍵中的所有資料行。
雜湊索引需要索引鍵(進行雜湊)以在索引中尋找。
如果索引鍵包含兩個數據行,且WHERE子句只提供第一個資料行,資料庫引擎就不會有完整的索引鍵可進行雜湊。
如此會導致索引掃描查詢計劃。
如果使用雜湊索引,且唯一索引鍵的數目是資料列計數的100倍(含)以上,請考慮加大值區計數,以避免資料列鏈結過長,或者改用非叢集索引。
宣告考量
雜湊索引只能存在於記憶體最佳化資料表上。
它無法存在於以磁碟為基礎的資料表上。
雜湊索引可以宣告為︰
UNIQUE,或者可以預設為非唯一。
NONCLUSTERED,這是預設值。
以下是在CREATETABLE陳述式之外建立雜湊索引的語法範例︰
ALTERTABLEMyTable_memop
ADDINDEXix_hash_Column2UNIQUE
HASH(Column2)WITH(BUCKET_COUNT=64);
資料列版本和記憶體回收
在記憶體最佳化資料表中,當資料列受到UPDATE影響時,資料表會建立資料列的更新版本。
在更新交易期間,其他工作階段或許能夠讀取舊版資料列,藉此避免發生與資料列鎖定相關聯的效能低落。
雜湊索引可能也有不同版本的項目來容納更新。
不再需要較舊的版本之後,記憶體回收(GC)執行緒會周遊值區及其連結清單來清除舊項目。
如果連結清單鏈結長度很短,GC執行緒的執行效能更好。
如需詳細資訊,請參閱記憶體內部OLTP垃圾收集。
Memory-Optimized非叢集索引設計指導方針
非叢集索引是記憶體最佳化資料表中可能有的索引類型之一。
如需詳細資訊,請參閱記憶體最佳化資料表的索引。
適用範圍:SQLServer(所有支援的版本)AzureSQLDatabaseAzureSQL受控執行個體
記憶體內部非叢集索引架構
記憶體中的非叢集索引會使用稱為Bw樹狀結構的資料結構來執行,該結構原本是在2011中由MicrosoftResearch所設想和描述。
Bw樹狀目錄是B型樹狀結構的鎖定和無閂鎖變化。
如需詳細資訊,請參閱Bw樹狀目錄:適用于新硬體平臺的B型樹狀目錄。
在非常高層級中,可以將Bw樹狀結構視為依頁面識別碼整理的頁面對應(PidMap)、配置和重複使用頁面識別碼的設施(PidAlloc)以及在頁面對應中連結的一組頁面。
這三個高階子元件組成Bw樹狀目錄的基本內部結構。
結構類似一般B型樹狀結構,因為每個頁面都有一組排序的索引鍵值,而索引中的層級各自指向較低的層級,而分葉層級指向資料列。
但是,有數個相異之處。
就像雜湊索引,多個資料列可以連結在一起(版本)。
層級之間的分頁指標是邏輯頁面識別碼,也就是頁面對應表中的位移,依次具有每個頁面的實體位址。
索引頁沒有就地更新。
為此推出新的差異頁面。
頁面更新不需要閂鎖或鎖定。
索引頁沒有固定的大小。
每個非分葉層級頁面中所述的索引鍵值是它所指向子系包含的最大值,而且每個資料列也會包含該頁面的邏輯頁識別碼。
在分葉層級頁面以及索引鍵值中,包含資料列的實體位址。
點查閱類似B型樹狀結構,唯一不同的是,因為頁面只會以單一方向連結,SQLServerDatabaseEngine所以會遵循右邊的頁面指標,其中每個非分葉頁面都有其子項目的最高值,而不是b型樹狀結構中的最小值。
如果分葉層級頁面必須變更,SQLServerDatabaseEngine不會修改頁面本身。
而是SQLServerDatabaseEngine會建立描述變更的差異記錄,並將它附加至前一頁。
然後,它也會將該上一頁的頁面對應表格位址更新為差異記錄的位址,該位址現在會變成此頁面的實體位址。
有三種不同的作業可以用來管理Bw樹狀目錄的結構:合併、分割和合併。
差異匯總
因為差異記錄的長鏈結可能表示搜尋過索引時要周遊很長的鏈結,所以它最後可能會降低搜尋效能。
如果新的差異記錄新增到已有16個項目的鏈結,則差異記錄中的變更會彙總至所參考的索引頁,並重建該頁面,包括觸發彙總之新差異記錄指出的變更。
最近重建的頁面上會有相同的頁面識別碼,但有新的記憶體位址。
分割頁面
Bw型樹狀結構中的索引頁可視需要從儲存單一資料列成長,最多儲存8KB。
一旦索引頁成長至8KB,新插入單一資料列就會分割索引頁。
若為內部分頁,這表示沒有可以新增其他索引鍵值的空間,若為分葉頁面,這表示資料列可能太大,頁面無法容納所有的差異記錄。
分葉頁面頁面標頭中的統計資料資訊會追蹤需要多少空間才能合併差異記錄,並會在新增每個新的差異記錄時調整該資訊。
分割作業是在兩個不可部分完成的步驟中完成。
下圖中,假設分葉頁面會強制分割,因為插入值為5的索引鍵,而且有非分葉頁面指向目前分葉層級頁(索引鍵值4)的結尾。
步驟1:配置兩個新的頁面P1和P2,並將資料列從舊的P1頁面分割到這些新的頁面,包括新插入的資料列。
頁面對應表中新的位置是用來儲存頁面P2的實體位址。
P1和P2這些頁面目前還無法存取任何並行作業。
此外,已設定從P1到P2的邏輯指標。
接著,在一個不可部分完成的步驟中更新頁面對應表,將指標從舊的P1變更為新的P1。
步驟2:非分葉頁面指向P1,但是沒有從非分葉頁面指向P2的直接指標。
只能透過P1連線到P2。
若要從非分葉頁面建立P2的指標,請配置新的非分葉頁面(內部索引頁)、複製所有舊的非分葉頁面的資料列,再新增新的資料列指向P2。
完成後,在一個不可部分完成的步驟中更新頁面對應表,將指標從舊的非分葉頁面變更為新的非分葉頁面。
合併頁面
當DELETE作業產生的頁面小於頁面大小上限(目前為8KB)的10%,或只有單一資料列時,該頁面就會合併到接續的頁面中。
從頁面刪除一個資料列時,會新增此項刪除的差異記錄。
此外,進行檢查,以判斷索引頁(非分葉頁面)是否可合併。
這項檢查會確認刪除資料列後的剩餘空間,是否小於頁面大小上限的10%。
如果不符合條件,就會在三個不可部分完成的步驟中執行合併。
下圖中,假設DELETE作業會刪除索引鍵值10。
步驟1:建立代表索引鍵值10(藍色三角形)的差異頁面,它在非分葉頁面Pp1中的指標設定為新的差異頁面。
另建立特殊的合併差異頁面(綠色三角形),連結指向差異頁面。
在這個階段,任何並行交易都看不到這兩種頁面(差異頁面和合併差異頁面)。
在某個不可部分完成的步驟中,頁面對應表中的分葉層級頁P1的指標會更新指向合併差異頁面。
這個步驟之後,Pp1中的索引鍵值10項目現在會指向合併差異頁面。
步驟2:必須移除非分葉頁面Pp1中代表索引鍵值7的資料列,並更新索引鍵值10項目指向P1。
若要這樣做,要配置新的非分葉頁面Pp2,並複製Pp1中除代表索引鍵值7以外的所有資料列,然後更新索引鍵值10的資料列指向頁面P1。
完成之後,在一個不可部分完成的步驟中,更新指向Pp1的頁面對應表項目指向Pp2。
無法再連線到Pp1。
步驟3:合併分葉層級頁面P2和P1,並移除差異頁面。
若要這樣做,要配置新的頁面P3,合併P1和P2的資料列,在新的P3中包含差異頁面變更。
然後,在一個不可部分完成的步驟中,更新指向P1頁面的頁面對應表項目指向頁面P3。
效能考量
利用不相等述詞查詢記憶體最佳化資料表時,非叢集索引的效能比非叢集雜湊索引的效能好。
注意
記憶體最佳化資料表中的資料行,可以是雜湊索引和非叢集索引的一部分。
提示
當非叢集索引索引鍵資料行中的某個資料行有許多重複的值時,效能會因為更新、插入及刪除而降低。
在此情況下,改善效能的其中一種方法就是在非叢集索引中加入其他資料行。
後續步驟
從下列文章深入瞭解索引設計和相關主題:
CREATEINDEX(Transact-SQL)
重新組織與重建索引
分割資料表與索引
記憶體最佳化資料表的索引
資料行存放區索引概觀
計算資料行的索引
使用遺漏索引建議來微調非叢集索引
本文內容
延伸文章資訊
- 1索引(Indexing) - 技術服務小百科
索引(Indexing) ... 索引又稱引得,舊稱韻編、通檢、備檢、便檢、檢目、玉鍵、針線等。 ... 綜觀來說,索引即為把文字資料(或非書資源)中包括的各種專名(人名 ...
- 2索引_百度百科
索引是為了加速對錶中數據行的檢索而創建的一種分散的存儲結構。索引是針對表而建立的,它是由數據頁面以外的索引頁面組成的,每個索引頁面中的行都會含有邏輯指針,以便 ...
- 3目錄、書目、目次及索引之概念釐清 - 全國新書資訊網
「索引(index)」及「書後索引(book index)」等,是我們向來耳熟能詳的圖書 ... 首先查考館藏之圖書資訊學語彙詞典及參考工具書指南中,針對這幾個語詞的定義與範疇.
- 4SQL Server 和Azure SQL 索引架構和設計指南
定義索引時,請考慮是否要以遞增或遞減順序儲存索引鍵資料行的資料。 遞增是預設值,而且會維持與舊版資料庫引擎的相容性。 CREATE INDEX、CREATE TABLE ...
- 5索引- 維基百科,自由的百科全書