1.HDEL

从 key 指定的哈希集中移除指定的域。在哈希集中不存在的域将被忽略。

如果 key 指定的哈希集不存在,它将被认为是一个空的哈希集,该命令将返回0。

时间复杂度:O(N) N是被删除的字段数量

  1. 127.0.0.1:> hset myhash field1 "foo"
  2. (integer)
  3. 127.0.0.1:> hdel myhash field1
  4. (integer)
  5. 127.0.0.1:>

源码解析

  1. // t_hash.c,
  2. void hdelCommand(client *c) {
  3. robj *o;
  4. int j, deleted = , keyremoved = ;
  5.  
  6. if ((o = lookupKeyWriteOrReply(c,c->argv[],shared.czero)) == NULL ||
  7. checkType(c,o,OBJ_HASH)) return;
  8. // 循环删除给定字段列表
  9. for (j = ; j < c->argc; j++) {
  10. if (hashTypeDelete(o,c->argv[j]->ptr)) {
  11. deleted++;
  12. // 当没有任何元素后,直接将key删除
  13. if (hashTypeLength(o) == ) {
  14. dbDelete(c->db,c->argv[]);
  15. keyremoved = ;
  16. break;
  17. }
  18. }
  19. }
  20. if (deleted) {
  21. signalModifiedKey(c->db,c->argv[]);
  22. notifyKeyspaceEvent(NOTIFY_HASH,"hdel",c->argv[],c->db->id);
  23. if (keyremoved)
  24. notifyKeyspaceEvent(NOTIFY_GENERIC,"del",c->argv[],
  25. c->db->id);
  26. server.dirty += deleted;
  27. }
  28. addReplyLongLong(c,deleted);
  29. }
  30. // 具体删除 field, 同样区分编码类型,不同处理逻辑
  31. /* Delete an element from a hash.
  32. * Return 1 on deleted and 0 on not found. */
  33. int hashTypeDelete(robj *o, sds field) {
  34. int deleted = ;
  35.  
  36. if (o->encoding == OBJ_ENCODING_ZIPLIST) {
  37. unsigned char *zl, *fptr;
  38.  
  39. zl = o->ptr;
  40. fptr = ziplistIndex(zl, ZIPLIST_HEAD);
  41. if (fptr != NULL) {
  42. // ziplist 删除,依次删除 field, value
  43. fptr = ziplistFind(fptr, (unsigned char*)field, sdslen(field), );
  44. if (fptr != NULL) {
  45. // ziplistDelete 为原地删除,所以只要调用2次,即把kv删除
  46. zl = ziplistDelete(zl,&fptr);
  47. zl = ziplistDelete(zl,&fptr);
  48. o->ptr = zl;
  49. deleted = ;
  50. }
  51. }
  52. } else if (o->encoding == OBJ_ENCODING_HT) {
  53. if (dictDelete((dict*)o->ptr, field) == C_OK) {
  54. deleted = ;
  55.  
  56. /* Always check if the dictionary needs a resize after a delete. */
  57. // hash 删除的,可能需要进行缩容操作,这种处理方法相对特殊些
  58. if (htNeedsResize(o->ptr)) dictResize(o->ptr);
  59. }
  60.  
  61. } else {
  62. serverPanic("Unknown hash encoding");
  63. }
  64. return deleted;
  65. }
  66. // server.c, 是否需要进行 resize
  67. int htNeedsResize(dict *dict) {
  68. long long size, used;
  69.  
  70. size = dictSlots(dict);
  71. used = dictSize(dict);
  72. // HASHTABLE_MIN_FILL=10, 即使用率小于 1/10 时,可以进行缩容操作了
  73. return (size && used && size > DICT_HT_INITIAL_SIZE &&
  74. (used*/size < HASHTABLE_MIN_FILL));
  75. }

2.HEXISTS

返回hash里面field是否存在

时间复杂度:O(1)

  1. 127.0.0.1:> hset myhash field1 "foo"
  2. (integer)
  3. 127.0.0.1:> hexists myhash field1
  4. (integer)
  5. 127.0.0.1:> hexists myhash field2
  6. (integer)
  7. 127.0.0.1:>

源码解析

  1. void hexistsCommand(client *c) {
  2. robj *o;
  3. if ((o = lookupKeyReadOrReply(c,c->argv[],shared.czero)) == NULL ||
  4. checkType(c,o,OBJ_HASH)) return;
  5.  
  6. addReply(c, hashTypeExists(o,c->argv[]->ptr) ? shared.cone : shared.czero);
  7. }
  1. hashTypeExists
  1. int hashTypeExists(robj *o, sds field) {
  2. if (o->encoding == OBJ_ENCODING_ZIPLIST) {
  3. unsigned char *vstr = NULL;
  4. unsigned int vlen = UINT_MAX;
  5. long long vll = LLONG_MAX;
  6.  
  7. if (hashTypeGetFromZiplist(o, field, &vstr, &vlen, &vll) == ) return ;
  8. } else if (o->encoding == OBJ_ENCODING_HT) {
  9. if (hashTypeGetFromHashTable(o, field) != NULL) return ;
  10. } else {
  11. serverPanic("Unknown hash encoding");
  12. }
  13. return ;
  14. }

ziplist的类型判断

  1. /* Get the value from a ziplist encoded hash, identified by field.
  2. * Returns -1 when the field cannot be found. */
  3. int hashTypeGetFromZiplist(robj *o, sds field,
  4. unsigned char **vstr,
  5. unsigned int *vlen,
  6. long long *vll)
  7. {
  8. unsigned char *zl, *fptr = NULL, *vptr = NULL;
  9. int ret;
  10.  
  11. serverAssert(o->encoding == OBJ_ENCODING_ZIPLIST);
  12.  
  13. zl = o->ptr;
  14. fptr = ziplistIndex(zl, ZIPLIST_HEAD);
  15. if (fptr != NULL) {
  16. fptr = ziplistFind(fptr, (unsigned char*)field, sdslen(field), );
  17. if (fptr != NULL) {
  18. /* Grab pointer to the value (fptr points to the field) */
  19. vptr = ziplistNext(zl, fptr);
  20. serverAssert(vptr != NULL);
  21. }
  22. }
  23.  
  24. if (vptr != NULL) {
  25. ret = ziplistGet(vptr, vstr, vlen, vll);
  26. serverAssert(ret);
  27. return ;
  28. }
  29.  
  30. return -;
  31. }

ziplistFind

  1. /* Find pointer to the entry equal to the specified entry. Skip 'skip' entries
  2. * between every comparison. Returns NULL when the field could not be found. */
  3. unsigned char *ziplistFind(unsigned char *p, unsigned char *vstr, unsigned int vlen, unsigned int skip) {
  4. int skipcnt = ;
  5. unsigned char vencoding = ;
  6. long long vll = ;
  7.  
  8. while (p[] != ZIP_END) {
  9. unsigned int prevlensize, encoding, lensize, len;
  10. unsigned char *q;
  11.  
  12. ZIP_DECODE_PREVLENSIZE(p, prevlensize);
  13. ZIP_DECODE_LENGTH(p + prevlensize, encoding, lensize, len);
  14. q = p + prevlensize + lensize;
  15.  
  16. if (skipcnt == ) {
  17. /* Compare current entry with specified entry */
  18. if (ZIP_IS_STR(encoding)) {
  19. if (len == vlen && memcmp(q, vstr, vlen) == ) {
  20. return p;
  21. }
  22. } else {
  23. /* Find out if the searched field can be encoded. Note that
  24. * we do it only the first time, once done vencoding is set
  25. * to non-zero and vll is set to the integer value. */
  26. if (vencoding == ) {
  27. if (!zipTryEncoding(vstr, vlen, &vll, &vencoding)) {
  28. /* If the entry can't be encoded we set it to
  29. * UCHAR_MAX so that we don't retry again the next
  30. * time. */
  31. vencoding = UCHAR_MAX;
  32. }
  33. /* Must be non-zero by now */
  34. assert(vencoding);
  35. }
  36.  
  37. /* Compare current entry with specified entry, do it only
  38. * if vencoding != UCHAR_MAX because if there is no encoding
  39. * possible for the field it can't be a valid integer. */
  40. if (vencoding != UCHAR_MAX) {
  41. long long ll = zipLoadInteger(q, encoding);
  42. if (ll == vll) {
  43. return p;
  44. }
  45. }
  46. }
  47.  
  48. /* Reset skip count */
  49. skipcnt = skip;
  50. } else {
  51. /* Skip entry */
  52. skipcnt--;
  53. }
  54.  
  55. /* Move to next entry */
  56. p = q + len;
  57. }
  58.  
  59. return NULL;
  60. }

hashtable类型的

  1. /* Get the value from a hash table encoded hash, identified by field.
  2. * Returns NULL when the field cannot be found, otherwise the SDS value
  3. * is returned. */
  4. sds hashTypeGetFromHashTable(robj *o, sds field) {
  5. dictEntry *de;
  6.  
  7. serverAssert(o->encoding == OBJ_ENCODING_HT);
  8.  
  9. de = dictFind(o->ptr, field);
  10. if (de == NULL) return NULL;
  11. return dictGetVal(de);
  12. }

dictFInd

  1. dictEntry *dictFind(dict *d, const void *key)
  2. {
  3. dictEntry *he;
  4. uint64_t h, idx, table;
  5.  
  6. if (dictSize(d) == ) return NULL; /* dict is empty */
  7. if (dictIsRehashing(d)) _dictRehashStep(d);
  8. h = dictHashKey(d, key);
  9. for (table = ; table <= ; table++) {
  10. idx = h & d->ht[table].sizemask;
  11. he = d->ht[table].table[idx];
  12. while(he) {
  13. if (key==he->key || dictCompareKeys(d, key, he->key))
  14. return he;
  15. he = he->next;
  16. }
  17. if (!dictIsRehashing(d)) return NULL;
  18. }
  19. return NULL;
  20. }

3.HGETALL、HKEYS、HVALS

HGetAll:返回 key 指定的哈希集中所有的字段和值。返回值中,每个字段名的下一个是它的值,所以返回值的长度是哈希集大小的两倍

时间复杂度:O(N)

HKeys:返回 key 指定的哈希集中所有字段的名字。

时间复杂度:O(N)

HVals:返回 key 指定的哈希集中所有字段的值。

时间复杂度:O(N)

  1. 127.0.0.1:> hset myhash field1 "Hello"
  2. (integer)
  3. 127.0.0.1:> hset myhash field2 "World"
  4. (integer)
  5. 127.0.0.1:> hkeys myhash
  6. ) "field1"
  7. ) "field2"
  8. 127.0.0.1:> hgetall myhash
  9. ) "field1"
  10. ) "Hello"
  11. ) "field2"
  12. ) "World"
  13. 127.0.0.1:> hvals myhash
  14. ) "Hello"
  15. ) "World"
  16. 127.0.0.1:>

源码解析

  1. void hkeysCommand(client *c) {
  2. genericHgetallCommand(c,OBJ_HASH_KEY);
  3. }
  4.  
  5. void hvalsCommand(client *c) {
  6. genericHgetallCommand(c,OBJ_HASH_VALUE);
  7. }
  8.  
  9. void hgetallCommand(client *c) {
  10. genericHgetallCommand(c,OBJ_HASH_KEY|OBJ_HASH_VALUE);
  11. }
  1. void genericHgetallCommand(client *c, int flags) {
  2. robj *o;
  3. hashTypeIterator *hi;
  4. int length, count = ;
  5.  
  6. if ((o = lookupKeyReadOrReply(c,c->argv[],shared.emptymap[c->resp]))
  7. == NULL || checkType(c,o,OBJ_HASH)) return;
  8.  
  9. /* We return a map if the user requested keys and values, like in the
  10. * HGETALL case. Otherwise to use a flat array makes more sense. */
  11. length = hashTypeLength(o);
  12. if (flags & OBJ_HASH_KEY && flags & OBJ_HASH_VALUE) {
  13. addReplyMapLen(c, length);
  14. } else {
  15. addReplyArrayLen(c, length);
  16. }
  17.  
  18. hi = hashTypeInitIterator(o);
  19. while (hashTypeNext(hi) != C_ERR) {
  20. if (flags & OBJ_HASH_KEY) {
  21. addHashIteratorCursorToReply(c, hi, OBJ_HASH_KEY);
  22. count++;
  23. }
  24. if (flags & OBJ_HASH_VALUE) {
  25. addHashIteratorCursorToReply(c, hi, OBJ_HASH_VALUE);
  26. count++;
  27. }
  28. }
  29.  
  30. hashTypeReleaseIterator(hi);
  31.  
  32. /* Make sure we returned the right number of elements. */
  33. if (flags & OBJ_HASH_KEY && flags & OBJ_HASH_VALUE) count /= ;
  34. serverAssert(count == length);
  35. }

4.HLEN

返回 key 指定的哈希集包含的字段的数量。

时间复杂度:O(1)

  1. 127.0.0.1:> hlen myhash
  2. (integer)
  3. 127.0.0.1:>
  1. void hlenCommand(client *c) {
  2. robj *o;
  3.  
  4. if ((o = lookupKeyReadOrReply(c,c->argv[],shared.czero)) == NULL ||
  5. checkType(c,o,OBJ_HASH)) return;
  6.  
  7. addReplyLongLong(c,hashTypeLength(o));
  8. }
  1. /* Return the number of elements in a hash. */
  2. unsigned long hashTypeLength(const robj *o) {
  3. unsigned long length = ULONG_MAX;
  4.  
  5. if (o->encoding == OBJ_ENCODING_ZIPLIST) {
  6. length = ziplistLen(o->ptr) / ;
  7. } else if (o->encoding == OBJ_ENCODING_HT) {
  8. length = dictSize((const dict*)o->ptr);
  9. } else {
  10. serverPanic("Unknown hash encoding");
  11. }
  12. return length;
  13. }

Redis系列(九):数据结构Hash之HDEL、HEXISTS、HGETALL、HKEYS、HLEN、HVALS命令的更多相关文章

  1. Redis系列(九):数据结构Hash源码解析和HSET、HGET命令

    2.源码解析 1.相关命令如下: {"hset",hsetCommand,,"wmF",,NULL,,,,,}, {"hsetnx",hse ...

  2. Redis系列-存储篇hash主要操作函数小结

    阳光透过玻璃,洒在身上,一杯暖茶在手,说不尽的安逸自得,让我有种想再写篇blog的冲动.上篇主要谈了string,这里谈谈hash吧!hash是一些列key value(field value)的映射 ...

  3. Redis系列(九)--几道面试题

    这里只是一点面试题,想了解更多,可以查看本人的Redis系列:https://www.cnblogs.com/huigelaile/category/1461895.html 1.Redis和Memc ...

  4. Redis系列二 - 数据结构

    前言 redis作为我们开发的一大神器,我们接触肯定不会少,但是很多同学也许只会存储String类型的值,这是非常不合理的.在这里,将带大家认识Redis的5中数据结构. 1.问:Redis有那些数据 ...

  5. Redis系列(九):Redis的事务机制

    提到事务,相信大家都不陌生,事务的ACID四大特性,也是面试时经常问的,不过一般情况下,我们可能想到的是传统关系型数据库的事务,其实,Redis也是提供了事务机制的,本篇博客就来讲解下Redis的事务 ...

  6. redis 系列6 数据结构之字典(下)

    一.概述 接着上篇继续,这篇把数据结构之字典学习完, 这篇知识点包括:哈希算法,解决键冲突, rehash , 渐进式rehash,字典API. 1.1 哈希算法 当一个新的键值对 需要添加到字典里面 ...

  7. redis 系列8 数据结构之整数集合

    一.概述 整数集合(intset)是集合键的底层实现之一, 当一个集合只包含整数值元素,并且这个集合元素数量不多时, Redis就会使用整数集合作为集合键的底层实现.下面创建一个只包含5个元素的集合键 ...

  8. redis 系列7 数据结构之跳跃表

    一.概述 跳跃表(skiplist)是一种有序数据结构,它通过在每个节点中维持多个指向其他节点的指针,从而达到快速访问节点的目的.在大部分情况下,跳跃表的效率可以和平衡树(关系型数据库的索引就是平衡树 ...

  9. redis 系列5 数据结构之字典(上)

    一. 概述 字典又称符号表(symbol table),关联数组(associative array), 映射(map),是一种用于保存键值对(key-value pair)的抽象数据结构.在字典中, ...

随机推荐

  1. Java实现 LeetCode 462 最少移动次数使数组元素相等 II

    462. 最少移动次数使数组元素相等 II 给定一个非空整数数组,找到使所有数组元素相等所需的最小移动数,其中每次移动可将选定的一个元素加1或减1. 您可以假设数组的长度最多为10000. 例如: 输 ...

  2. Java实现 LeetCode 215. 数组中的第K个最大元素

    215. 数组中的第K个最大元素 在未排序的数组中找到第 k 个最大的元素.请注意,你需要找的是数组排序后的第 k 个最大的元素,而不是第 k 个不同的元素. 示例 1: 输入: [3,2,1,5,6 ...

  3. Java实现 蓝桥杯VIP 算法提高 递归倒置字符数组

    算法提高 递归倒置字符数组 时间限制:1.0s 内存限制:512.0MB 问题描述 完成一个递归程序,倒置字符数组.并打印实现过程 递归逻辑为: 当字符长度等于1时,直接返回 否则,调换首尾两个字符, ...

  4. Java实现蓝桥杯G将军

    G将军有一支训练有素的军队,这个军队除开G将军外,每名士兵都有一个直接上级(可能是其他士兵,也可能是G将军).现在G将军将接受一个特别的任务,需要派遣一部分士兵(至少一个)组成一个敢死队,为了增加队员 ...

  5. Fiddler13模拟弱网络环境测试

    前言现在的Android软件,基本上都会有网络请求,有些APP需要频繁的传输数据时对于网络请求的稳定性和在特殊网络条件下的兼容性有要求,但是我们在测试的时候又很难模拟那种弱网络差网络的情况,今天就给大 ...

  6. 关于64位W7下怎么学习汇编语言的一些心得!

    出处:http://tieba.baidu.com/p/2277546332 1.首先下载DOSBOX,它的作用就是让你在64位下使用32.16位的软件.如果不使用DOSBOX就会出现程序不兼容的对话 ...

  7. TypeScript使用体会(一)

    typescript使用体会 近期接手了一个公司项目是由TS写的,第一次用在这里做一下简单的使用体会 个人觉得TS与JS相差不多,只是多了一些约束(可能自己还没体会到精髓) typescript是Ja ...

  8. 蒲公英 · JELLY技术周刊 Vol.10 为什么你还不使用 TypeScript

    登高远眺 天高地迥,觉宇宙之无穷 基础技术 直播延迟?为什么流媒体直播的延迟那么高 随着通信技术的发展,视频点播.直播业务也逐渐兴起.在这些业务形态中,流媒体技术扮演着重要的角色.但在实际使用中,即便 ...

  9. vuex登录验证及保持登录状态

    不知道vuex的可以先看一下 vuex官方文档,这里就不赘述了. 实现思路:假设我们现在想要访问自己在博客园里写的博客,这时候服务器需要知道当前用户是谁,才能确定我们是否有访问权限并正确地返回我们需要 ...

  10. centos7.2挂载硬盘攻略

    远程SSH登录上CentOS服务器后,进行如下操作: 提醒:挂载操作会清空数据,请确认挂载盘无数据或者未使用 第一步:列出所有磁盘 命令: ll /dev/disk/by-path ll /dev/d ...