Redis數據類型之字典
標籤(空格分隔): redis
redis的字典
字典又稱符號表(symbol table),關聯數組(associative array),或者映射(map)。是用於保存鍵值對的一種抽象數據結構。
字典的key是唯一的,對鍵值對的操作基本都是基於key來操作的。redis中的數據庫底層是使用字典來實現的,對於數據庫的增刪改查都是基於字典來實現的。redis的哈希鍵也是基於字典來實現的。
具體的實現是在src下的dict.h和dict.c文件
字典的數據結構
哈希表結點
/*
* 哈希表節點
*/
typedef struct dictEntry {
// 鍵
void *key;
// 值
union {
void *val;
uint64_t u64;
int64_t s64;
} v;
// 指向下個哈希表節點,形成鏈表
struct dictEntry *next;
} dictEntry;
哈希表的數據結構
/*
* 哈希表
*/
typedef struct dictht {
// 哈希表數組
dictEntry **table;
// 哈希表大小
unsigned long size;
// 哈希表大小掩碼,用於計算索引值
// 總是等於 size - 1
unsigned long sizemask;
// 該哈希表已有節點的數量
unsigned long used;
} dictht;
字典的數據結構
/*
* 字典
*/
typedef struct dict {
// 類型特定函數
dictType *type;
// 私有數據
void *privdata;
// 哈希表
dictht ht[2];
// rehash 索引
// 當 rehash 不在進行時,值爲 -1
int rehashidx; /* rehashing not in progress if rehashidx == -1 */
// 目前正在運行的安全迭代器的數量
int iterators; /* number of iterators currently running */
} dict;
其中的dictType是一個struct
/*
* 字典類型特定函數
*/
typedef struct dictType {
// 計算哈希值的函數
unsigned int (*hashFunction)(const void *key);
// 複製鍵的函數
void *(*keyDup)(void *privdata, const void *key);
// 複製值的函數
void *(*valDup)(void *privdata, const void *obj);
// 對比鍵的函數
int (*keyCompare)(void *privdata, const void *key1, const void *key2);
// 銷燬鍵的函數
void (*keyDestructor)(void *privdata, void *key);
// 銷燬值的函數
void (*valDestructor)(void *privdata, void *obj);
} dictType;
// 哈希表
dictht ht[2];
這裏的ht是一個包含兩項數據的數組,數組的每一項都是一個dictht哈希表,一般情況下,只使用ht[0],ht[1]是在rehash的情況下使用。
哈希算法
當需要向字典添加一對鍵值對時,程序首先根據字典的key計算出hash值和索引值,然後根據索引值,將包含新鍵值對的哈希表結點放到哈希數組指定的索引位置上。
redis計算hash值和索引的方法如下
// 計算給定鍵的哈希值
#define dictHashKey(d, key) (d)->type->hashFunction(key)
// 計算索引值
idx = h & d->ht[table].sizemask;
哈希衝突
當兩個或者兩個以上的鍵被分配到哈希數組的同一個索引上,被稱爲哈希衝突。
哈希衝突的解決方法:
- 開放定址法(包括線性探查法、線性補償探測法、隨機探測)
- 拉鍊法,又稱鏈地址法
redis如何解決哈希衝突?
鏈地址法,每個哈希表結點都有一個next指針域,多個哈希表結點可以構成一個單鏈表,被分配到同一個索引的鍵可以使用這個next指針連起來,從而解決哈希衝突。