操作命令
- hset key不存在执行创建操作,存在执行更新操作
- hsetnx key不存在才执行命令
- hget 获取field上的value值
- HINCRBY 加上多少数据,当value可以被解释成数字的情况
- HINCRBYFLOAT增加浮点数
- hstrlen 统计filed的value的长度
- hexists key是否存在这个field
- hdel 删除field,想删除key的话还是给用del删除key
- hlen 统计key中filed的个数
- hmset 给一个key设置多个filed
- hkeys 获得key的所有filed值
- hvals获得field的value值
- hgetall field和value一起获得
一些原理知识
redis:6.2.1
dict位置: src/dict.h srcdict.c src/t_hash.c
字典采用渐进rehash的方式,哈希算法采用的murmurHashX算法(X表示版本号)。哈希因子 = used / size。
什么时候扩容?
- redis服务器没有启动BGSAVE 或者 BGWRITEAOF命令的时候并且哈希因子>=1的时候
- 开启上面命令,哈希因子>=5
因为这两个命令会开启子进程,而写时拷贝就会占用大量内存,这也是一个细节的优化
渐进哈希
查和新增的时候都会判断是否在rehash,如果是的话就会稍带从老的哈希表挪到新的哈希表中,稍带指得就是渐进rehash.rehash是把这个哈希表的索引后面的链表都挪到新的哈希表上面去。找索引给10次机会,如果10次机会都用完还没找到合适的机会就会返回了。
源码
这部分源码还可以,都能看懂,介绍一下这些struct成员变量吧都是在src/dich.h能找到的
dict这是个字典,type可以理解为回调函数。privdata暂时没有用到,dictht就是两块哈希表表。rehashidx不等于1的时候说明在渐进rehash中,rehashidx意为在第一块哈希表中哈希到那里的下标。pauserehash看名字猜测是判断是否终止了渐进哈希。
typedef struct dict {
dictType *type;// 设置哈希函数的
void *privdata;
dictht ht[2];// 两块哈希表
long rehashidx; /* rehashing not in progress if rehashidx == -1 */
int16_t pauserehash; /* If >0 rehashing is paused (<0 indicates coding error) */
} dict;
dictht table就是存放key-value的了,size是开辟容量,sizemash = size - 1用作哈希值的计算。used包含了多少元素,包括了开链法的拥有的元素
typedef struct dictht {
dictEntry **table;
unsigned long size;
unsigned long sizemask;
unsigned long used;
} dictht;
dictType存放那一系列函数指针了
typedef struct dictType {
uint64_t (*hashFunction)(const void *key);// 获得hash值
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);
int (*expandAllowed)(size_t moreMem, double usedRatio);// 扩容
} dictType;
在t_hash.c文件中我们可以看到我们经常使用的hset、hget的使用。void hsetCommand(client *c),暂时能力有限刚接触源码就大致看懂了。因为最开始很好奇hset这个的field是怎么存的,今天终于知道了
key还是保存在哈希表里,而val还是一个哈希表,val->key就是field字段了,而val->val就是存放val了。