字典,又称符号表(symbol table)、关联数组或映射(map),是一种用于保存键值对(key-value)的抽象数据结构。
在字典中,一个键(key)可以和一个值(value)进行关联(或者说将键值对隐射为值),这些关联的键和值就称为键值对。
字典中的每个键都是独一无二的,程序可以在字典中根据键查找与之关联的值,或者通过键来跟新值,又或者根据键来删除整个键值对,等等。
字典经常作为一种数据结构内置在很多高级语言里面,但Redis所使用的C语言并没有内置这种数据结构,因此Redis构建了自己的字典实现。
字典在Redis中的应用相当广泛,比如Redis的数据库就是使用字典来作为底层实现的,对数据库的增删改查操作也是构建在对字典的操作之上的。
举个例子:当我们执行命令:
redis>SET msg "hello world"
ok
在数据库中创建了一个键为“msg”, 值为“hello world”的键值对时,这个键值对就是保存在代表数据库的字典里面的。
除了用于表示数据库之外,字典还是哈希键的底层实现之一,当一个哈希键包含的键值对比较多,又或者键值对中的元素都是比较长的字符串时,Redis就会使用字典作为哈希键的底层实现。
举个例子:website是一个包含了10086个键值对的哈希键,这个哈希键的键都是一些数据库的名字,而键的值就是数据库的主页地址。
redis>HLEN website
(integer)10086
redis>HGETALL website
1)"Redis"
2)"Redis.io"
3)"MongoDB"
4)"MongoDB.org"
.....
website的底层实现就是一个字典,字典包含了10086个键值对。
typedef struct dictht { //哈希表数组 dictEntry **table; //哈希表大小 unsigned long size; //哈希表大小掩码,用于计算索引值 //总是等于size-1 unsigned long sizemask; //哈希表已有节点的数量 unsigned long used; }dictht;table是一个数组,用于指向dict.h/dictEntry结构的指针,每个dictEntry结构保存着一个键值对。size属性记录了哈希表的大小,也即table数组的大小,而used属性则记录了哈希表目前已有节点(键值对的数量)。sizeMask属性的值总是等于size-1,这个属性和哈希值一起决定一个键应该被放到table数组的哪个索引上面。
typedef struct dictEntry { //键 void *key; //值 union{ void *val; uint64_tu64; int64_ts64; }v; //指向下一个哈希表节点,形成链表 struct dictEntry *next; }dictEntry;key属性保存着键值对中的键,而v属性则保存着键值对中的值,其中键值对的值可以是一个指针,或者是一个uint64_t整数,又或者是一个int64_t整数。
typedef struct dict { //特定类型函数 dictType *type; //私有数据 void *privadata; //哈希表 dictht ht[2]; //rehash索引 //当rehash不在进行时,值为-1 rehashindex = -1; }dict;type属性是一个指向dictType结构的指针,每个dictType结构保存了一簇用于操作特定类型键值对的函数,Redis会为用途不同的字典设置不同的类型特定函数。
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;ht属性是一个包含了两个项的数组,数组中的每个项都是一个dictht哈希表,一般情况下,字典只使用ht[0]哈希表,ht[1]哈希表只会在对ht[0]哈希表进行rehash时使用。