Redis系列(九):数据结构Hash之HDEL、HEXISTS、HGETALL、HKEYS、HLEN、HVALS命令
1.HDEL
从 key 指定的哈希集中移除指定的域。在哈希集中不存在的域将被忽略。
如果 key 指定的哈希集不存在,它将被认为是一个空的哈希集,该命令将返回0。
时间复杂度:O(N) N是被删除的字段数量
127.0.0.1:> hset myhash field1 "foo"
(integer)
127.0.0.1:> hdel myhash field1
(integer)
127.0.0.1:>
源码解析
// t_hash.c,
void hdelCommand(client *c) {
robj *o;
int j, deleted = , keyremoved = ; if ((o = lookupKeyWriteOrReply(c,c->argv[],shared.czero)) == NULL ||
checkType(c,o,OBJ_HASH)) return;
// 循环删除给定字段列表
for (j = ; j < c->argc; j++) {
if (hashTypeDelete(o,c->argv[j]->ptr)) {
deleted++;
// 当没有任何元素后,直接将key删除
if (hashTypeLength(o) == ) {
dbDelete(c->db,c->argv[]);
keyremoved = ;
break;
}
}
}
if (deleted) {
signalModifiedKey(c->db,c->argv[]);
notifyKeyspaceEvent(NOTIFY_HASH,"hdel",c->argv[],c->db->id);
if (keyremoved)
notifyKeyspaceEvent(NOTIFY_GENERIC,"del",c->argv[],
c->db->id);
server.dirty += deleted;
}
addReplyLongLong(c,deleted);
}
// 具体删除 field, 同样区分编码类型,不同处理逻辑
/* Delete an element from a hash.
* Return 1 on deleted and 0 on not found. */
int hashTypeDelete(robj *o, sds field) {
int deleted = ; if (o->encoding == OBJ_ENCODING_ZIPLIST) {
unsigned char *zl, *fptr; zl = o->ptr;
fptr = ziplistIndex(zl, ZIPLIST_HEAD);
if (fptr != NULL) {
// ziplist 删除,依次删除 field, value
fptr = ziplistFind(fptr, (unsigned char*)field, sdslen(field), );
if (fptr != NULL) {
// ziplistDelete 为原地删除,所以只要调用2次,即把kv删除
zl = ziplistDelete(zl,&fptr);
zl = ziplistDelete(zl,&fptr);
o->ptr = zl;
deleted = ;
}
}
} else if (o->encoding == OBJ_ENCODING_HT) {
if (dictDelete((dict*)o->ptr, field) == C_OK) {
deleted = ; /* Always check if the dictionary needs a resize after a delete. */
// hash 删除的,可能需要进行缩容操作,这种处理方法相对特殊些
if (htNeedsResize(o->ptr)) dictResize(o->ptr);
} } else {
serverPanic("Unknown hash encoding");
}
return deleted;
}
// server.c, 是否需要进行 resize
int htNeedsResize(dict *dict) {
long long size, used; size = dictSlots(dict);
used = dictSize(dict);
// HASHTABLE_MIN_FILL=10, 即使用率小于 1/10 时,可以进行缩容操作了
return (size && used && size > DICT_HT_INITIAL_SIZE &&
(used*/size < HASHTABLE_MIN_FILL));
}
2.HEXISTS
返回hash里面field是否存在
时间复杂度:O(1)
127.0.0.1:> hset myhash field1 "foo"
(integer)
127.0.0.1:> hexists myhash field1
(integer)
127.0.0.1:> hexists myhash field2
(integer)
127.0.0.1:>
源码解析
void hexistsCommand(client *c) {
robj *o;
if ((o = lookupKeyReadOrReply(c,c->argv[],shared.czero)) == NULL ||
checkType(c,o,OBJ_HASH)) return;
addReply(c, hashTypeExists(o,c->argv[]->ptr) ? shared.cone : shared.czero);
}
hashTypeExists
int hashTypeExists(robj *o, sds field) {
if (o->encoding == OBJ_ENCODING_ZIPLIST) {
unsigned char *vstr = NULL;
unsigned int vlen = UINT_MAX;
long long vll = LLONG_MAX;
if (hashTypeGetFromZiplist(o, field, &vstr, &vlen, &vll) == ) return ;
} else if (o->encoding == OBJ_ENCODING_HT) {
if (hashTypeGetFromHashTable(o, field) != NULL) return ;
} else {
serverPanic("Unknown hash encoding");
}
return ;
}
ziplist的类型判断
/* Get the value from a ziplist encoded hash, identified by field.
* Returns -1 when the field cannot be found. */
int hashTypeGetFromZiplist(robj *o, sds field,
unsigned char **vstr,
unsigned int *vlen,
long long *vll)
{
unsigned char *zl, *fptr = NULL, *vptr = NULL;
int ret; serverAssert(o->encoding == OBJ_ENCODING_ZIPLIST); zl = o->ptr;
fptr = ziplistIndex(zl, ZIPLIST_HEAD);
if (fptr != NULL) {
fptr = ziplistFind(fptr, (unsigned char*)field, sdslen(field), );
if (fptr != NULL) {
/* Grab pointer to the value (fptr points to the field) */
vptr = ziplistNext(zl, fptr);
serverAssert(vptr != NULL);
}
} if (vptr != NULL) {
ret = ziplistGet(vptr, vstr, vlen, vll);
serverAssert(ret);
return ;
} return -;
}
ziplistFind
/* Find pointer to the entry equal to the specified entry. Skip 'skip' entries
* between every comparison. Returns NULL when the field could not be found. */
unsigned char *ziplistFind(unsigned char *p, unsigned char *vstr, unsigned int vlen, unsigned int skip) {
int skipcnt = ;
unsigned char vencoding = ;
long long vll = ; while (p[] != ZIP_END) {
unsigned int prevlensize, encoding, lensize, len;
unsigned char *q; ZIP_DECODE_PREVLENSIZE(p, prevlensize);
ZIP_DECODE_LENGTH(p + prevlensize, encoding, lensize, len);
q = p + prevlensize + lensize; if (skipcnt == ) {
/* Compare current entry with specified entry */
if (ZIP_IS_STR(encoding)) {
if (len == vlen && memcmp(q, vstr, vlen) == ) {
return p;
}
} else {
/* Find out if the searched field can be encoded. Note that
* we do it only the first time, once done vencoding is set
* to non-zero and vll is set to the integer value. */
if (vencoding == ) {
if (!zipTryEncoding(vstr, vlen, &vll, &vencoding)) {
/* If the entry can't be encoded we set it to
* UCHAR_MAX so that we don't retry again the next
* time. */
vencoding = UCHAR_MAX;
}
/* Must be non-zero by now */
assert(vencoding);
} /* Compare current entry with specified entry, do it only
* if vencoding != UCHAR_MAX because if there is no encoding
* possible for the field it can't be a valid integer. */
if (vencoding != UCHAR_MAX) {
long long ll = zipLoadInteger(q, encoding);
if (ll == vll) {
return p;
}
}
} /* Reset skip count */
skipcnt = skip;
} else {
/* Skip entry */
skipcnt--;
} /* Move to next entry */
p = q + len;
} return NULL;
}
hashtable类型的
/* Get the value from a hash table encoded hash, identified by field.
* Returns NULL when the field cannot be found, otherwise the SDS value
* is returned. */
sds hashTypeGetFromHashTable(robj *o, sds field) {
dictEntry *de; serverAssert(o->encoding == OBJ_ENCODING_HT); de = dictFind(o->ptr, field);
if (de == NULL) return NULL;
return dictGetVal(de);
}
dictFInd
dictEntry *dictFind(dict *d, const void *key)
{
dictEntry *he;
uint64_t h, idx, table; if (dictSize(d) == ) return NULL; /* dict is empty */
if (dictIsRehashing(d)) _dictRehashStep(d);
h = dictHashKey(d, key);
for (table = ; table <= ; table++) {
idx = h & d->ht[table].sizemask;
he = d->ht[table].table[idx];
while(he) {
if (key==he->key || dictCompareKeys(d, key, he->key))
return he;
he = he->next;
}
if (!dictIsRehashing(d)) return NULL;
}
return NULL;
}
3.HGETALL、HKEYS、HVALS
HGetAll:返回 key 指定的哈希集中所有的字段和值。返回值中,每个字段名的下一个是它的值,所以返回值的长度是哈希集大小的两倍
时间复杂度:O(N)
HKeys:返回 key 指定的哈希集中所有字段的名字。
时间复杂度:O(N)
HVals:返回 key 指定的哈希集中所有字段的值。
时间复杂度:O(N)
127.0.0.1:> hset myhash field1 "Hello"
(integer)
127.0.0.1:> hset myhash field2 "World"
(integer)
127.0.0.1:> hkeys myhash
) "field1"
) "field2"
127.0.0.1:> hgetall myhash
) "field1"
) "Hello"
) "field2"
) "World"
127.0.0.1:> hvals myhash
) "Hello"
) "World"
127.0.0.1:>
源码解析
void hkeysCommand(client *c) {
genericHgetallCommand(c,OBJ_HASH_KEY);
}
void hvalsCommand(client *c) {
genericHgetallCommand(c,OBJ_HASH_VALUE);
}
void hgetallCommand(client *c) {
genericHgetallCommand(c,OBJ_HASH_KEY|OBJ_HASH_VALUE);
}
void genericHgetallCommand(client *c, int flags) {
robj *o;
hashTypeIterator *hi;
int length, count = ;
if ((o = lookupKeyReadOrReply(c,c->argv[],shared.emptymap[c->resp]))
== NULL || checkType(c,o,OBJ_HASH)) return;
/* We return a map if the user requested keys and values, like in the
* HGETALL case. Otherwise to use a flat array makes more sense. */
length = hashTypeLength(o);
if (flags & OBJ_HASH_KEY && flags & OBJ_HASH_VALUE) {
addReplyMapLen(c, length);
} else {
addReplyArrayLen(c, length);
}
hi = hashTypeInitIterator(o);
while (hashTypeNext(hi) != C_ERR) {
if (flags & OBJ_HASH_KEY) {
addHashIteratorCursorToReply(c, hi, OBJ_HASH_KEY);
count++;
}
if (flags & OBJ_HASH_VALUE) {
addHashIteratorCursorToReply(c, hi, OBJ_HASH_VALUE);
count++;
}
}
hashTypeReleaseIterator(hi);
/* Make sure we returned the right number of elements. */
if (flags & OBJ_HASH_KEY && flags & OBJ_HASH_VALUE) count /= ;
serverAssert(count == length);
}
4.HLEN
返回 key 指定的哈希集包含的字段的数量。
时间复杂度:O(1)
127.0.0.1:> hlen myhash
(integer)
127.0.0.1:>
void hlenCommand(client *c) {
robj *o;
if ((o = lookupKeyReadOrReply(c,c->argv[],shared.czero)) == NULL ||
checkType(c,o,OBJ_HASH)) return;
addReplyLongLong(c,hashTypeLength(o));
}
/* Return the number of elements in a hash. */
unsigned long hashTypeLength(const robj *o) {
unsigned long length = ULONG_MAX; if (o->encoding == OBJ_ENCODING_ZIPLIST) {
length = ziplistLen(o->ptr) / ;
} else if (o->encoding == OBJ_ENCODING_HT) {
length = dictSize((const dict*)o->ptr);
} else {
serverPanic("Unknown hash encoding");
}
return length;
}
Redis系列(九):数据结构Hash之HDEL、HEXISTS、HGETALL、HKEYS、HLEN、HVALS命令的更多相关文章
- Redis系列(九):数据结构Hash源码解析和HSET、HGET命令
2.源码解析 1.相关命令如下: {"hset",hsetCommand,,"wmF",,NULL,,,,,}, {"hsetnx",hse ...
- Redis系列-存储篇hash主要操作函数小结
阳光透过玻璃,洒在身上,一杯暖茶在手,说不尽的安逸自得,让我有种想再写篇blog的冲动.上篇主要谈了string,这里谈谈hash吧!hash是一些列key value(field value)的映射 ...
- Redis系列(九)--几道面试题
这里只是一点面试题,想了解更多,可以查看本人的Redis系列:https://www.cnblogs.com/huigelaile/category/1461895.html 1.Redis和Memc ...
- Redis系列二 - 数据结构
前言 redis作为我们开发的一大神器,我们接触肯定不会少,但是很多同学也许只会存储String类型的值,这是非常不合理的.在这里,将带大家认识Redis的5中数据结构. 1.问:Redis有那些数据 ...
- Redis系列(九):Redis的事务机制
提到事务,相信大家都不陌生,事务的ACID四大特性,也是面试时经常问的,不过一般情况下,我们可能想到的是传统关系型数据库的事务,其实,Redis也是提供了事务机制的,本篇博客就来讲解下Redis的事务 ...
- redis 系列6 数据结构之字典(下)
一.概述 接着上篇继续,这篇把数据结构之字典学习完, 这篇知识点包括:哈希算法,解决键冲突, rehash , 渐进式rehash,字典API. 1.1 哈希算法 当一个新的键值对 需要添加到字典里面 ...
- redis 系列8 数据结构之整数集合
一.概述 整数集合(intset)是集合键的底层实现之一, 当一个集合只包含整数值元素,并且这个集合元素数量不多时, Redis就会使用整数集合作为集合键的底层实现.下面创建一个只包含5个元素的集合键 ...
- redis 系列7 数据结构之跳跃表
一.概述 跳跃表(skiplist)是一种有序数据结构,它通过在每个节点中维持多个指向其他节点的指针,从而达到快速访问节点的目的.在大部分情况下,跳跃表的效率可以和平衡树(关系型数据库的索引就是平衡树 ...
- redis 系列5 数据结构之字典(上)
一. 概述 字典又称符号表(symbol table),关联数组(associative array), 映射(map),是一种用于保存键值对(key-value pair)的抽象数据结构.在字典中, ...
随机推荐
- Java实现 LeetCode 654 最大二叉树(递归)
654. 最大二叉树 给定一个不含重复元素的整数数组.一个以此数组构建的最大二叉树定义如下: 二叉树的根是数组中的最大元素. 左子树是通过数组中最大值左边部分构造出的最大二叉树. 右子树是通过数组中最 ...
- Java实现 LeetCode 551 学生出勤记录 I(暴力大法好)
551. 学生出勤记录 I 给定一个字符串来代表一个学生的出勤记录,这个记录仅包含以下三个字符: 'A' : Absent,缺勤 'L' : Late,迟到 'P' : Present,到场 如果一个 ...
- Java实现蓝桥杯算法提高 哥德巴赫猜想
试题 算法提高 哥德巴赫猜想 资源限制 时间限制:1.0s 内存限制:256.0MB 问题描述 根据所给函数(判断一个整数是否是素数),然后依托该函数,将输入N内的偶数(6-N),输出为两个素数之和( ...
- Java实现P2102 -- 正整数序列
P2102 – 正整数序列 给定正整数n, 你的任务是用最少的操作次数把序列1,2,-,n中的所有数都变成0.每次操作可从序列中选择一个或多个整数, 同时减去一个相同的正整数.比如,1,2,3可以把2 ...
- Java实现 蓝桥杯VIP 算法提高 递归倒置字符数组
算法提高 递归倒置字符数组 时间限制:1.0s 内存限制:512.0MB 问题描述 完成一个递归程序,倒置字符数组.并打印实现过程 递归逻辑为: 当字符长度等于1时,直接返回 否则,调换首尾两个字符, ...
- 记一次discuz修改首页图片路径问题
1.找到图片路径拼装文件 首先打开根目录下的template目录找到首页文件 打开后找到图片列表的拼装位置 // 链接示例: <!--{eval $imagelistkey = getforum ...
- DMR对讲机利用XLX网络联网通信
By 申建军 BD8SN 2018-9-29 本文适用于运行G4KLX DMRGateway的MMDVM热点和中继,目前国内绝大部分的热点用户都是使用pi-star镜像的MMDVM热点,均可按此设置. ...
- zabbix 监控交换机文本/日志信息
简介 公司有台路由设备,需要每天去核对用户量,查看信息等.为了避免重复的工作量,就通过zabbix来完成这个重复性的工作. 先说下思路,首先要创建一个脚本,能从zabbix-server 或者zabb ...
- Python3.7解释器+sublime Text3编辑器+案例打包软件+Python语言程序设计基础教材
编辑器:https://nsyw.lanzous.com/i7lcdyh Python3.7 https://nsyw.lanzous.com/i7a299c 案例 https://nsyw.lanz ...
- AntD框架的upload组件上传图片时使用customRequest方法自定义上传行为
本次做后台管理系统,采用的是 AntD 框架.涉及到图片的上传,用的是AntD的 upload 组件. 我在上一篇文章<AntD框架的upload组件上传图片时使用customRequest方法 ...