In-Memory:哈希索引

SQL Server 2016支持哈希查找,用戶能夠在內存優化表(Memory-Optimized Table)上建立哈希索引(Hash Index),使用Hash 查找算法,實現數據的極速查找。在使用上,Hash Index 和B-Tree索引的區別是:Hash Index 是無序查找,Index Key必須所有做爲查找的條件,多個鍵值之間是邏輯與關係,而且只能執行等值查找,而B-Tree索引是有序查找,不須要Index Key都做爲查找,只須要前序字段都存在,還能夠進行大於、小於、等於等比較操做;在存儲結構上,Hash Index使用Hash Table實現,存在Hash 衝突,而B-Tree索引的結構是B-Tree結構,存在頁拆分和索引碎片等問題。算法

一,Hash 查找算法

在《數據結構》課程中,Hash查找的算法是:以關鍵字k爲自變量,經過一個映射函數h,計算出對應的函數值y=h(k)(y稱做哈希值,或哈希地址),根據函數值y,將關鍵字k存儲在數組(bucket數組)所指向的鏈表中。在進行哈希查找時,根據關鍵字,使用相同的函數h計算哈希地址h(k),而後直接尋址相應的Hash bucket,直接到對應的鏈表中取出數據。所以,Hash 查找算法的數據結構由Hash Bucket數組,映射函數f和數據鏈表組成,一般將Bucket數組和數據鏈表稱做Hash Table,如圖,Hash Table由5個buckets和7個數據結點組成:sql

哈希查找的時間複雜度是O(n/m),n是指數據結點的數量,m是bucket的數量,在理想狀況下,Hash Bucket足夠多,Hash函數不產生重複的Hash Value,哈希查找的時間複雜度最優到達O(1),可是,在實際應用中,哈希函數有必定的概率出現重複的哈希地址,產生哈希衝突,時間複雜度會低於O(n/m);在最差的狀況下,時間複雜度是O(n)。數組

二,Hash Index的結構

Hash Index使用Hash查找算法實現,SQL Server內置Hash函數,用於全部的Hash Index,所以,Hash Index就是Hash Table,由Hash Buckets數組和數據行鏈表組成。建立Hash Index時,經過Hash函數計算Index Key的Hash地址,Hash地址不一樣的數據行指向不一樣的Bucket,Hash地址相同的數據行指向相同的Bucket,若是多個數據行的Hash地址相同,都指向同一個Bucket,那麼將這些數據行連接在一塊兒,組成一個鏈表。數據結構

A hash index consists of an array of pointers, and each element of the array is called a hash bucket. The index key column in each row has a hash function applied to it, and the result of the function determines which bucket is used for that row. All key values that hash to the same value (have the same result from the hash function) are accessed from the same pointer in the hash index and are linked together in a chain. When a row is added to the table, the hash function is applied to the index key value in the row. If there is duplication of key values, the duplicates will always generate the same function result and thus will always be in the same chain.app

舉例說明,假定哈希函數是h(k)=Length(k),用於計算Index Key的字符個數,在內存優化表(Name,City)上建立Hash Index,Index ptr指向鏈表中的下一個數據行,若是沒有下一個數據行,那麼該指針爲NULL:ide

1,以Name爲Index Key建立Hash Index函數

第一個數據行的Name是「Jane」,HashValue是4,將該行數據映射到下標爲4的Bucket中(Bucket數組的第五個元素),因爲該數據行是第一個數據結點,Index ptr爲NULL。性能

 

第二個數據行,Name值是「Greg」,HashValue是4,映射到下標爲4的Bucket中,和第一個數據行連接在一塊兒,組成一個鏈表(Chain),插入數據結點時,使用頭部插入法,新的數據節點做爲頭結點,將頭節點的Index ptr(next pointer)指針指向數據鏈表的第一個數據結點,如圖,新的頭結點「Greg」的Index ptr指向第一個數據行「Jane」。優化

 

2,建立第二個Hash Index,以City爲Index Keyui

當建立第二個Hash Index時,每一個數據行結構中包含兩個Index ptr指針,都用於指向下一個數據節點(Next Pointer):第一個Index ptr用於Index Key爲Name的Hash Index,當出現相同的Hash Value時,該指針指向鏈表中下一個數據行,使數據行連接到一塊兒組成鏈表;第二個Index ptr用於Index Key爲City的Hash Index,指向鏈表中下一個數據行。

所以,當建立一個新的Hash Index時,在數據結構上,SQL Server須要建立Hash Buckets數組,並在每一個數據行中增長一個Index ptr字段,根據Index Key爲Index ptr賦值,組成一個新數據行鏈表,可是數據行的數量保持不變。

3,Hash 函數

在建立Hash Index時,不須要編寫Hash 函數,SQL Server內置Hash函數:

  • 內置的Hash函數產生的HashValue是隨機和不可預測的,適用於全部的Hash Index;
  • 內置的Hash函數是肯定性的,相同的Index Key老是映射到相同的Bucket;
  • 有必定的概率,多個Index Key會映射到相同的bucket中;
  • 哈希函數是均衡的,產生的Hash Value服從泊松分佈;

泊松分佈不是均勻分佈,Index Key不是均勻地分佈在Hash bucket數組中。例如,有n個Hash Bucket,n個不一樣的Index Key,泊松分佈產生的結果是:大約有1/3的Hash Bucket是空的,大約1/3的Hash bucket存儲一個Index Key,剩下1/3的Hash Buckets存儲2個Index Key。

4,Hash Index的鏈表長度

不一樣的Index Key,通過hash函數映射以後,可能生成相同的Hash Value,映射到相同的bucket中,產生 Hash 衝突。Hash算法,將映射到相同Bucket的多個Index Key組成一個鏈表,鏈表越長,Hash Index查找性能越差。

在DMV:sys.dm_db_xtp_hash_index_stats (Transact-SQL)中,表示Hash Index鏈長的字段有:avg_chain_length 和 max_chain_length ,鏈長應保持在2左右;鏈長過大,代表太多的數據行被映射到相同的Bucket中,這會顯著影響Hash Index的查詢性能,致使鏈長過大的緣由是:

  • 總的Bucket數量少,致使不一樣的Index Key映射到相同的Bucket上;
  • 若是空的Bucket數量大,但鏈長過大,這說明,Hash Index存在大量重複的Index Key;相同的Index Key被映射到相同的bucket;

三,建立Hash Index

在內存優化表上建立Index,不能使用Create Index命令,SQL Server 2016支持兩種方式建立索引:

1,在建立內存優化表時建立Hash Index

建立Hash Index的語法是:

INDEX index_name [ NONCLUSTERED ] HASH WITH (BUCKET_COUNT = bucket_count)  

建立Hash Index的示例:

--create memory optimized table
create table [dbo].[products]
(
    [ProductID] [bigint] not null,
    [Name] [varchar](64) not null,
    [Price] decimal(10,2) not null,
    [Unit] varchar(16) not null,
    [Description] [varchar](max) null,
    constraint [PK__Products_ProductID] primary key nonclustered hash ([ProductID])with (bucket_count=2000000)
    ,index idx_Products_Price  nonclustered([Price] desc)
    ,index idx_Products_Unit nonclustered hash(Unit) with(bucket_count=40000)
)
with(memory_optimized=on,durability= schema_and_data)
go
View Code

2,使用Alter Table命令建立Hash Index

alter table [dbo].[products]
add index hash_idx_Products_Name nonclustered hash(name)with(bucket_count=40000);

四,Hash Index的特色

總結Hash Index的特色:

  • Hash Index使用Hash Table組織Index 結構,每個數據節點都包含一個指針,指向數據行的內存地址;
  • Hash Index是無序的,適合作單個數據行的Index Seek;
  • 只有當Hash Index Key所有出如今Filter中,SQL Server纔會使用Hash Index Seek操做查找相應的數據行,若是缺失任意一個Index Column,那麼SQL Server都會執行Full Table Scan以獲取符合條件的數據行。例如,建立Hash Index時指定N個column,那麼SQL Server對這N個column計算Hash  Value,映射到相應的bucket上,因此,只有當這N個Column都存在時,才能定位到對應的bucket,進而查找相應的數據結點;
Hash indexes require values for all index key columns in order to compute the hash value, and locate the corresponding rows in the hash table. Therefore, if a query includes equality predicates for only a subset of the index keys in the WHERE clause, SQL Server cannot use an index seek to locate the rows corresponding to the predicates in the WHERE clause.

 

參考文檔:

Hash Indexes

Guidelines for Using Indexes on Memory-Optimized Tables

Troubleshooting Common Performance Problems with Memory-Optimized Hash Indexes

Linux內核中的hash與bucket

相關文章
相關標籤/搜索