簡體   English   中英

SQL Server索引性能 - 長列

[英]SQL Server Index performance - long column

在SQL Server(2005+)中,我需要索引一個列(僅限完全匹配) nvarchar(2000+) 什么是最具可擴展性,高性能的方法?

在SQL Server(2005+)中,使用以下類型對列進行索引的實際區別是什么:

  • nvarchar(2000)
  • char(40)
  • binary(16)

例如,對索引 binary(16)列的查找是否明顯快於對索引的 nvarchar(2000)的查找? 如果是這樣,多少錢?

顯然小在某些方面總是更好,但我不太熟悉SQL Server如何優化其索引以了解它如何處理長度。

當然,二進制文件(16)會更快 - 只需進行最快的計算:

  • SQL Server頁面始終為8K
  • 如果每個條目有16個字節,則可以在頁面上存儲500個條目
  • 如果每個條目有4000個字節(nvarchar),那么每頁最多會有2個條目(最糟糕的情況是,如果您的NVARCHAR(2000)已完全填充)

如果你有一個包含100'000個條目的表,你必須有200個頁面用於二進制(16)鍵的索引,而你需要50'000個頁面用於nvarchar(2000)相同的索引

即使只是添加I / O來讀取和掃描所有這些頁面,也會殺死你可能擁有的任何性能........

更新:
對於我常用的索引,我盡可能地避免復合索引 - 從其他表引用它們只是相當混亂(WHERE子句與幾個相等比較)。

此外,定期檢查和維護您的索引 - 如果您有超過30%的碎片,重建 - 如果您有5-30%的碎片,重新組織。 查看經過充分測試的自動DB索引維護腳本, 網址http://sqlfool.com/2009/06/index-defrag-script-v30/

對於SQL Server表上的群集密鑰 ,請嘗試避免GUID,因為它們本質上是隨機的,因此可能導致大量索引碎片,從而損害性能。 此外,雖然不是一個硬性要求,但請嘗試確保您的群集密鑰是唯一的 - 如果不是,SQL Server將為其添加一個四字節的唯一性。 此外,聚簇鍵被添加到每個非聚集索引中的每個條目中 - 因此在聚簇鍵中,擁有一個小的,唯一的,穩定的(不變的)列是非常重要的(最好是它不斷增加) ,這給你最好的特點和性能 - > INT IDENTITY是完美的)。

你從錯誤的方向思考這個問題:

  • 創建滿足性能目標所需的索引
  • 不要創建不需要的索引

無論列是binary(16)還是nvarchar(2000)都沒有什么區別,因為你不要只是無緣無故地添加索引。

不要讓索引選擇決定您的列類型。 如果需要索引nvarchar(2000)考慮全文索引或為列和索引添加哈希值。


根據您的更新,我可能會使用HashBytes()函數和索引創建校驗和列或計算列。 請注意,校驗和與加密哈希不同,因此您更有可能發生沖突,但您也可以匹配文本的全部內容,並且它將首先使用索引進行過濾。 HashBytes()不太可能發生沖突,但它仍然可能,因此您仍需要比較實際列。 對每個查詢和每次更改計算哈希值,HashBytes也更昂貴。

每個索引條目最多可以有900個字節,因此nvarchar(2000)不會飛。 最大的區別是索引深度 - 從根頁面到葉子頁面遍歷的頁面數。 所以,如果你需要搜索,你可以在CHECKSUM上索引,如下所示:

alter table recipe add text_checksum as checksum(recipe_text)
create index text_checksum_ind on recipe(text_checksum)

(例如,此處的計算列索引:加速查詢,添加業務規則 )不會給您完全匹配,只能很好地縮小搜索范圍。

當然,如果您需要強制執行唯一性,則必須使用觸發器。

另一個想法是將你的nvarchar壓縮為一個較小的二進制值,並對其進行索引,但是你能保證每個值總是壓縮到900字節或更少嗎?

索引最大長度無論如何都是900字節 ,所以你不能索引NVARCHAR(2000)。

索引鍵越大意味着索引頁面中的鍵越少,因此它會創建更大的樹,使用的磁盤越多,I / O越多,緩沖區拉動越多,緩存越少。 對於群集密鑰,這更糟糕,因為聚簇鍵值用作所有其他非群集索引的查找值,因此它會增加所有索引的大小。

最終,查詢中最常見的單一性能驅動指標是掃描/搜索的頁數。 這轉換為物理讀取(= I / O等待時間)或邏輯讀取(=緩存污染)。

除了空間考慮因素之外,數據類型在查詢行為中幾乎沒有差別。 char / varchar / nchar / nvarchar具有在比較時需要考慮的排序規則,但排序順序查找的成本通常不是決定因素。

最后但並非最不重要的,可能是最重要的因素,是您的應用程序訪問模式 索引查詢SARGable的列,必須維護優化程序未使用的索引絕對沒有任何好處。

有時你必須考慮並發問題,比如當你必須消除由同一記錄的不同更新訪問路徑引起的死鎖時

在帖子編輯后更新

使用持久性MD5哈希列:

create table foo (
    bar nvarchar(2000) not null, 
    [hash] as hashbytes('MD5', bar) persisted not null,
    constraint pk_hash unique ([hash]));
go


insert into foo (bar) values (N'Some text');
insert into foo (bar) values (N'Other text');
go

select * from foo
    where [hash] = hashbytes('MD5', N'Some text');
go

你必須非常小心你的搜索,哈希會因輸入的任何差異而大不相同,即。 如果你尋求Ascii參數而不是Unicode ...

如果你的桌子變大,你將有一個不錯的碰撞機會

實際上,最好是自己進行基准測試。 例如,以下腳本將通過4字節整數的索引搜索與通過50字節字符的搜索進行比較。 對於一個int(在INT列上構建的B樹的深度)和對char的4個讀取(在CHAR列上構建的B樹的深度),它是3次讀取。

CREATE TABLE dbo.NarrowKey(n INT NOT NULL PRIMARY KEY, m INT NOT NULL)
GO
DECLARE @i INT;
SET @i = 1;
INSERT INTO dbo.NarrowKey(n,m) SELECT 1,1;
WHILE @i<1024000 BEGIN
  INSERT INTO dbo.NarrowKey(n,m)
    SELECT n + @i, n + @i FROM dbo.NarrowKey;
  SET @i = @i * 2;
END;
GO
DROP TABLE dbo.WideKey
GO
CREATE TABLE dbo.WideKey(n CHAR(50) NOT NULL PRIMARY KEY, m INT NOT NULL)
GO
DECLARE @i INT;
SET @i = 1;
INSERT INTO dbo.WideKey(n,m) SELECT '1',1;
WHILE @i<1024000 BEGIN
  INSERT INTO dbo.WideKey(n,m)
    SELECT CAST((m + @i) AS CHAR(50)), n + @i FROM dbo.WideKey;
  SET @i = @i * 2;
END;
GO
SET STATISTICS IO ON;
SET STATISTICS TIME ON;
GO
SELECT * FROM dbo.NarrowKey WHERE n=123456
SELECT * FROM dbo.WideKey WHERE n='123456'

對於更寬的鍵,索引搜索速度要慢33%,但表格大4倍:

EXEC sp_spaceused 'dbo.NarrowKey';
-- 32K
EXEC sp_spaceused 'dbo.WideKey';
-- 136K

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM