Redisbook學習筆記(1)字典(1)

字典(dictionary),又名映射(map)或關聯數組(associative array), 它是一種抽象數據結
構,由一集鍵值對(key-value pairs)組成,各個鍵值對的鍵各不相同,程序能夠將新的鍵值對
添加到字典中,或者基於鍵進行查找、更新或刪除等操做。數據庫

 

字典的主要用途有如下兩個:
1. 實現數據庫鍵空間(key space)數組

2. 用做Hash 類型鍵的其中一種底層實現安全

 

實現數據庫鍵空間
Redis 是一個鍵值對數據庫,數據庫中的鍵值對就由字典保存:每一個數據庫都有一個與之相對
應的字典,這個字典被稱之爲鍵空間(key space)。
當用戶添加一個鍵值對到數據庫時(不論鍵值對是什麼類型),程序就將該鍵值對添加到鍵空
間;當用戶從數據庫中刪除一個鍵值對時,程序就會將這個鍵值對從鍵空間中刪除;等等。數據結構

 

用做Hash 類型鍵的其中一種底層實現
Redis 的Hash 類型鍵使用如下兩種數據結構做爲底層實現:
1. 字典;
2. 壓縮列表;
由於壓縮列表比字典更節省內存,因此程序在建立新Hash 鍵時,默認使用壓縮列表做爲底層
實現,當有須要時,程序纔會將底層實現從壓縮列表轉換到字典。ide

 

字典的實現
實現字典的方法有不少種:
 最簡單的就是使用鏈表或數組,可是這種方式只適用於元素個數很少的狀況下;
 要兼顧高效和簡單性,可使用哈希表;
 若是追求更爲穩定的性能特徵,而且但願高效地實現排序操做的話,則可使用更爲復
雜的平衡樹;
在衆多可能的實現中,Redis 選擇了高效且實現簡單的哈希表做爲字典的底層實現。
dict.h/dict 給出了這個字典的定義:函數

/*
* 字典
**
每一個字典使用兩個哈希表,用於實現漸進式rehash ,即哈希表擴容
*/
typedef struct dict {
// 特定於類型的處理函數
dictType *type;
// 類型處理函數的私有數據
void *privdata;
// 哈希表(2 個)
dictht ht[2];
// 記錄rehash 進度的標誌,值爲-1 表示rehash 未進行
int rehashidx;
// 當前正在運做的安全迭代器數量
int iterators;
} dict;

注意dict 類型使用了兩個指針分別指向兩個哈希表。
其中,0 號哈希表(ht[0])是字典主要使用的哈希表,而1 號哈希表(ht[1])則只有在程序
對0 號哈希表進行rehash 時才使用。性能

哈希表實現
字典所使用的哈希表實現由dict.h/dictht 類型定義:ui

/*
* 哈希表
*/
typedef struct dictht {
// 哈希表節點指針數組(俗稱桶,bucket)
dictEntry **table;
// 指針數組的大小
unsigned long size;
// 指針數組的長度掩碼,用於計算索引值
unsigned long sizemask;
// 哈希表現有的節點數量
unsigned long used;
} dictht;

table 屬性是一個數組,數組的每一個元素都是一個指向dictEntry 結構的指針。
每一個dictEntry 都保存着一個鍵值對,以及一個指向另外一個dictEntry 結構的指針:spa

/*
* 哈希表節點
*/
typedef struct dictEntry {
// 鍵
void *key;
// 值
union {
void *val;
uint64_t u64;
int64_t s64;
} v;
// 鏈日後繼節點
struct dictEntry *next;
} dictEntry;

next 屬性指向另外一個dictEntry 結構,多個dictEntry 能夠經過next 指針串連成鏈表,從
這裏能夠看出,dictht 使用鏈地址法來處理鍵碰撞:當多個不一樣的鍵擁有相同的哈希值時,哈
希表用一個鏈表將這些鍵鏈接起來。
下圖展現了一個由dictht 和數個dictEntry 組成的哈希表例子:指針

wKioL1LVSEeTpFxrAADhOazr_ic213.jpg

若是再加上以前列出的dict 類型,那麼整個字典結構能夠表示以下:

wKiom1LVSHmALDvWAAD7DusTTJY742.jpg

在上圖的字典示例中,字典雖然建立了兩個哈希表,但正在使用的只有0 號哈希表,這說明字
典未進行rehash 狀態。

 

建立新字典

wKiom1LVSOLQhSS3AAD7vuy5spI163.jpg

新建立的兩個哈希表都沒有爲table 屬性分配任何空間:
 ht[0]->table 的空間分配將在第一次往字典添加鍵值對時進行;
 ht[1]->table 的空間分配將在rehash 開始時進行;

 

添加鍵值對到字典
根據字典所處的狀態,將一個給定的鍵值對添加到字典可能會引發一系列複雜的操做:
 若是字典爲未初始化(也便是字典的0 號哈希表的table 屬性爲空),那麼程序須要對0
號哈希表進行初始化;
 若是在插入時發生了鍵碰撞,那麼程序須要處理碰撞;
 若是插入新元素使得字典知足了rehash 條件,那麼須要啓動相應的rehash 程序;
當程序處理完以上三種狀況以後,新的鍵值對纔會被真正地添加到字典上。
整個添加流程能夠用下圖表示:

wKioL1LVShjCy-JnAAFF_PDoi_k441.jpg

wKiom1LVSlbhsZeCAABfLA8zDRw223.jpg

在接下來的三節中,咱們將分別看到添加操做如何在如下三種狀況中執行:
1. 字典爲空;
2. 添加新鍵值對時發生碰撞處理;
3. 添加新鍵值對時觸發了rehash 操做;

 

~~每次遇到指針,就有點頭大,明天繼續吧!

相關文章
相關標籤/搜索