zoukankan      html  css  js  c++  java
  • Redis系列(十):数据结构Hash之HDEL、HEXISTS、HGETALL、HKEYS、HLEN、HVALS命令

    1.HDEL

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

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

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

    127.0.0.1:6379> hset myhash field1 "foo"
    (integer) 1
    127.0.0.1:6379> hdel myhash field1
    (integer) 1
    127.0.0.1:6379>

    源码解析

    // t_hash.c, 
    void hdelCommand(client *c) {
        robj *o;
        int j, deleted = 0, keyremoved = 0;
    
        if ((o = lookupKeyWriteOrReply(c,c->argv[1],shared.czero)) == NULL ||
            checkType(c,o,OBJ_HASH)) return;
        // 循环删除给定字段列表
        for (j = 2; j < c->argc; j++) {
            if (hashTypeDelete(o,c->argv[j]->ptr)) {
                deleted++;
                // 当没有任何元素后,直接将key删除
                if (hashTypeLength(o) == 0) {
                    dbDelete(c->db,c->argv[1]);
                    keyremoved = 1;
                    break;
                }
            }
        }
        if (deleted) {
            signalModifiedKey(c->db,c->argv[1]);
            notifyKeyspaceEvent(NOTIFY_HASH,"hdel",c->argv[1],c->db->id);
            if (keyremoved)
                notifyKeyspaceEvent(NOTIFY_GENERIC,"del",c->argv[1],
                                    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 = 0;
    
        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), 1);
                if (fptr != NULL) {
                    // ziplistDelete 为原地删除,所以只要调用2次,即把kv删除
                    zl = ziplistDelete(zl,&fptr);
                    zl = ziplistDelete(zl,&fptr);
                    o->ptr = zl;
                    deleted = 1;
                }
            }
        } else if (o->encoding == OBJ_ENCODING_HT) {
            if (dictDelete((dict*)o->ptr, field) == C_OK) {
                deleted = 1;
    
                /* 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*100/size < HASHTABLE_MIN_FILL));
    }

    2.HEXISTS

    返回hash里面field是否存在

    时间复杂度:O(1)

    127.0.0.1:6379> hset myhash field1 "foo"
    (integer) 1
    127.0.0.1:6379> hexists myhash field1
    (integer) 1
    127.0.0.1:6379> hexists myhash field2
    (integer) 0
    127.0.0.1:6379>

    源码解析

    void hexistsCommand(client *c) {
        robj *o;
        if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.czero)) == NULL ||
            checkType(c,o,OBJ_HASH)) return;
    
        addReply(c, hashTypeExists(o,c->argv[2]->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) == 0) return 1;
        } else if (o->encoding == OBJ_ENCODING_HT) {
            if (hashTypeGetFromHashTable(o, field) != NULL) return 1;
        } else {
            serverPanic("Unknown hash encoding");
        }
        return 0;
    }

    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), 1);
            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 0;
        }
    
        return -1;
    }

    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 = 0;
        unsigned char vencoding = 0;
        long long vll = 0;
    
        while (p[0] != 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 == 0) {
                /* Compare current entry with specified entry */
                if (ZIP_IS_STR(encoding)) {
                    if (len == vlen && memcmp(q, vstr, vlen) == 0) {
                        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 == 0) {
                        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) == 0) return NULL; /* dict is empty */
        if (dictIsRehashing(d)) _dictRehashStep(d);
        h = dictHashKey(d, key);
        for (table = 0; table <= 1; 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:6379> hset myhash field1 "Hello"
    (integer) 1
    127.0.0.1:6379> hset myhash field2 "World"
    (integer) 1
    127.0.0.1:6379> hkeys myhash
    1) "field1"
    2) "field2"
    127.0.0.1:6379> hgetall myhash
    1) "field1"
    2) "Hello"
    3) "field2"
    4) "World"
    127.0.0.1:6379> hvals myhash
    1) "Hello"
    2) "World"
    127.0.0.1:6379>

    源码解析

    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 = 0;
    
        if ((o = lookupKeyReadOrReply(c,c->argv[1],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 /= 2;
        serverAssert(count == length);
    }

    4.HLEN

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

    时间复杂度:O(1)

    127.0.0.1:6379> hlen myhash
    (integer) 2
    127.0.0.1:6379>
    void hlenCommand(client *c) {
        robj *o;
    
        if ((o = lookupKeyReadOrReply(c,c->argv[1],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) / 2;
        } else if (o->encoding == OBJ_ENCODING_HT) {
            length = dictSize((const dict*)o->ptr);
        } else {
            serverPanic("Unknown hash encoding");
        }
        return length;
    }
  • 相关阅读:
    bootstrap轮播组件之“如何关闭自动轮播”
    js分享功能
    设置省略号的取巧方法
    bootstrap-table表格插件的使用案例
    如果有帮到您,欢迎打赏
    IDEA导入Eclipse项目
    Centos nginx安装
    centos tomcat安装
    centos安装jdk
    Centos创建用户
  • 原文地址:https://www.cnblogs.com/vic-tory/p/13218762.html
Copyright © 2011-2022 走看看