• redis数据结构


    redis数据结构

    redis全名(Remote Dictionary Server),即远程字典服务

    redis的值的数据结构类型有String、List、Set、Hash、zset(sorted set,有序集合)、Bitmaps(位图)、HyperLogLogs

    注意:我使用的版本是6.0.10,不同版本可能略有差别

    redis中key的最大长度为512M

    对象

    server.h

    任何value对象都会被包装成一个redisObject,redisObject能指定value类型,编码方式等数据属性

    typedef struct redisObject {
       // 对象的类型 字符串、列表、集合、哈希表
        unsigned type:4;
       // 编码方式,也就是存储方式,提供多种方式来保存一个数据
        unsigned encoding:4;
       // 记录最后一次被访问的时间
        unsigned lru:LRU_BITS; /* LRU time (relative to global lru_clock) or
                                * LFU data (least significant 8 bits frequency
        // 引用计数                        * and most significant 16 bits access time). */

        int refcount;
       // 指向底层实现的指针
        void *ptr;
    } robj;
    • 1
    类型总览

    redis中的数据类型,即为redisObject的type属性

    // string
    #define OBJ_STRING 0    /* String object. */
    // list
    #define OBJ_LIST 1      /* List object. */
    // set
    #define OBJ_SET 2       /* Set object. */
    //zset
    #define OBJ_ZSET 3      /* Sorted set object. */
    //hash
    #define OBJ_HASH 4      /* Hash object. */
    // module
    #define OBJ_MODULE 5    /* Module object. */
    // stream
    #define OBJ_STREAM 6    /* Stream object. */
    • 1
    #可以使用type命令来查看该键所对应值的类型
    type key
    • 1
    redis的编码分类

    对应于redisObject的ecoding属性,encoding是决定对象底层实现数据结构的

    // 简单动态字符串  raw
    #define OBJ_ENCODING_RAW 0     /* Raw representation */
    // long类型的整数 int
    #define OBJ_ENCODING_INT 1     /* Encoded as integer */
    // 哈希表  hashtable
    #define OBJ_ENCODING_HT 2      /* Encoded as hash table */
    // 压缩  不再使用
    #define OBJ_ENCODING_ZIPMAP 3  /* Encoded as zipmap */
    // 双向链表  不再使用该结构
    #define OBJ_ENCODING_LINKEDLIST 4 /* No longer used: old list encoding. */
    // 压缩列表  ziplist
    #define OBJ_ENCODING_ZIPLIST 5 /* Encoded as ziplist */
    // 整数集合  intset
    #define OBJ_ENCODING_INTSET 6  /* Encoded as intset */
    // 跳表  skiplist
    #define OBJ_ENCODING_SKIPLIST 7  /* Encoded as skiplist */
    // embstr编码的简单动态字符串  embstr
    #define OBJ_ENCODING_EMBSTR 8  /* Embedded sds string encoding */
    // 快表  quicklist
    #define OBJ_ENCODING_QUICKLIST 9 /* Encoded as linked list of ziplists */
    // 流  stream
    #define OBJ_ENCODING_STREAM 10 /* Encoded as a radix tree of listpacks */
    • 1

    可以使用命令来查看存储方式

    object encoding key
    • 1

    来查看该key使用的是哪种编码

    127.0.0.1:6379set test_int 1
    OK
    127.0.0.1:6379> object encoding test_int
    "int"
    127.0.0.1:6379set test_raw zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz
    OK
    127.0.0.1:6379strlen test_raw
    (integer) 44
    127.0.0.1:6379> object encoding test_raw
    "embstr"
    127.0.0.1:6379> set test_raw zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz
    OK
    127.0.0.1:6379> object encoding test_raw
    "raw"
    127.0.0.1:6379> strlen test_raw
    (integer) 45
    # 查看key的内部结构和编码等信息   
    >debug object k4
      Value at:0x7ffc97c044f0 refcount:1 encoding:embstr serializedlength:3 lru:11351195 lru_seconds_idle:5363
    • 1

    String类型

    string类型属于单值单value,是一个可变的字节数组,就像StringBuilder似的,可以追加、获取长度、截取等操作

    扩容:当字符串长度小于1M时,扩容是现有空间进行加倍;当字符串长度超过1M,扩容只会多扩1M的空间。且字符串最长为512M

    源码结构

    sds.h其内部使用的是SDS简单动态字符串,保存了长度信息,获取字符串长度只需要O(1)

    struct __attribute__ ((__packed__)) sdshdr5 {
       // 存储了字符串的类型和长度,3位存类型,5位存长度
        unsigned char flags; /* 3 lsb of type, and 5 msb of string length */
        char buf[];
    };
    // 暂时应该是没有用到5的,只用到了8、16、32、64
    struct __attribute__ ((__packed__)) sdshdr8 {
      // header中进行存储len、alloc、flags
      // 当前使用空间大小,即字符串的长度
        uint8_t len; /* used */
      // 去掉header和结尾空字符后的最大空间
        uint8_t alloc; /* excluding the header and null terminator */
      // 8位的标记,3位存类型,5位没有使用
        unsigned char flags; /* 3 lsb of type, 5 unused bits */
      // 数组进行存储
        char buf[];
    };
    struct __attribute__ ((__packed__)) sdshdr16 {
        uint16_t len; /* used */
        uint16_t alloc; /* excluding the header and null terminator */
        unsigned char flags; /* 3 lsb of type, 5 unused bits */
        char buf[];
    };
    struct __attribute__ ((__packed__)) sdshdr32 {
        uint32_t len; /* used */
        uint32_t alloc; /* excluding the header and null terminator */
        unsigned char flags; /* 3 lsb of type, 5 unused bits */
        char buf[];
    };
    struct __attribute__ ((__packed__)) sdshdr64 {
        uint64_t len; /* used */
        uint64_t alloc; /* excluding the header and null terminator */
        unsigned char flags; /* 3 lsb of type, 5 unused bits */
        char buf[];
    };
    • 1
    编码格式
    编码对象
    OBJ_ENCODING_INT使用整数值实现的字符串对象
    OBJ_ENCODING_EMBSTR使用embstr编码的简单动态字符串实现的字符串对象
    OBJ_ENCODING_RAW使用简单动态字符串实现的字符串对象

    string有三种编码格式

    • int 整数值

    • embstr 用于存储短字符串的编码方式,字符串长度小于等于44字节使用,数据保存在一块连续的内存里

    • raw 字符串值长度大于44字节使用

    embstr和raw两种编码的区别

    embstr编码用于保存短字符串,与raw编码一样,都是使用redisObject结构和sdshdr结构来表示字符串对象,但是raw编码会调用两次内存分配函数来分别创建redisObject结构和sdshdr结构,而embstr编码则通过调用一次内存分配函数来分配一块连续的内存空间,空间中依次包含redisObject和sdshdr两个结构

    操作命令

    值拼接


    #
    给key对应的value进行拼接,返回的是拼接之后的字符长度
    append k1 qwe
    (integer) 5
    • 1

    值长度

    strlen k1
    • 1

    数字运算操作

    #加一
    INCR k2
    #减一
    DECR k2
    #加3,第二个参数是步长
    INCRBY k2 3
    #减2,第二个参数是步长
    DECRBY k2 2
    • 1

    注意:这几个命令只能对数字进行操作,范围是Long.MIN~Long.MAX

    获取值

    get k1

    #
    只获取该key所对应value的部分字符
    getrange k1 0 2
    • 1

    判断值是否存在

    # 返回0则为不存在
    exists test
    • 1

    设置值

    # set key value
    set test 123

    #
     setrange覆盖部分值  其中0为从下标为0的字符开始替换
    setrange k1 0 zx
    (integer) 5

    #
    设置值时设置过期时间
    #setex key seconds value
    setex k3 20 b

    #
    如果key不存在设置值,防止覆盖(set if not exists)
    #setnx key value
    setnx k1 1

    #
     设置key的值,并返回key的旧值
    getset k1 vv1
    • 1

    多值操作

    #同时set多个键值
    #mset key value [key value ...]
    mset k4 v4 k5 v5
    #同时获取多个键的值
    #mget key  [key ...]
    mget k4 k5 
    #同时set多个键值(全部不存在时才会设置值)
    #msetnx key value [key value ...]
    msetnx k6 v6 k7 k8
    • 1

    List类型

    List属于单值多value,链表用的是双向链表结构,list支持pop、push来操作头部和尾部,既可以用做栈也可以用做队列

    在3.2版本之前使用的是ziplist和linkedlist,在3.2版本之后使用的是quicklist

    源码结构
    quicklist结构
    typedef struct quicklist {
        quicklistNode *head;
        quicklistNode *tail;
        unsigned long count;        /* total count of all entries in all ziplists */
        unsigned long len;          /* number of quicklistNodes */
        int fill : QL_FILL_BITS;              /* fill factor for individual nodes */
        unsigned int compress : QL_COMP_BITS; /* depth of end nodes not to compress;0=off */
        unsigned int bookmark_count: QL_BM_BITS;
        quicklistBookmark bookmarks[];
    } quicklist;

    typedef struct quicklistNode {
        struct quicklistNode *prev;
        struct quicklistNode *next;
        unsigned char *zl; // 对应的是ziplist
        unsigned int sz;             /* ziplist size in bytes */
        unsigned int count : 16;     /* count of items in ziplist */
        unsigned int encoding : 2;   /* RAW==1 or LZF==2 */
        unsigned int container : 2;  /* NONE==1 or ZIPLIST==2 */
        unsigned int recompress : 1/* was this node previous compressed? */
        unsigned int attempted_compress : 1/* node can't compress; too small */
        unsigned int extra : 10/* more bits to steal for future usage */
    } quicklistNode;
    • 1
    ziplist结构

    压缩的双链表,以连续的空间来表示双链表,节省前驱和后继指针的空间,在小的list上,压缩效率明显,因为在普通的双链表中,前驱和后继指针在64位机器上分别占用8B,在ziplist.h和ziplist.c中定义

    typedef struct zlentry {
        unsigned int prevrawlensize; /* Bytes used to encode the previous entry len*/
        unsigned int prevrawlen;     /* Previous entry len. */
        unsigned int lensize;        /* Bytes used to encode this entry type/len.
                                        For example strings have a 1, 2 or 5 bytes
                                        header. Integers always use a single byte.*/

        unsigned int len;            /* Bytes used to represent the actual entry.
                                        For strings this is just the string length
                                        while for integers it is 1, 2, 3, 4, 8 or
                                        0 (for 4 bit immediate) depending on the
                                        number range. */

        unsigned int headersize;     /* prevrawlensize + lensize. */
        unsigned char encoding;      /* Set to ZIP_STR_* or ZIP_INT_* depending on
                                        the entry encoding. However for 4 bits
                                        immediate integers this can assume a range
                                        of values and must be range-checked. */

        unsigned char *p;            /* Pointer to the very start of the entry, that
                                        is, this points to prev-entry-len field. */

    } zlentry;

    typedef struct {
        /* When string is used, it is provided with the length (slen). */
        unsigned char *sval;
        unsigned int slen;
        /* When integer is used, 'sval' is NULL, and lval holds the value. */
        long long lval;
    } ziplistEntry;
    • 1

    而对于quicklist编码的定义是

    OBJ_ENCODING_QUICKLIST 9 /* Encoded as linked list of ziplists */
    • 1
    操作命令

    设置值

    #lpush(指从左边进,头部,先进后出,像栈)
    #lpush key element [element ...]
    lpush list1 1 2 3 4 5

    #
    rpush(指从右边进,尾部,先进先出,像队列)
    #rpush key element [element ...]
    rpush list2 1 2 3 4 5

    #
    给某个索引位置赋值
    #lset key index element
    lset list1 0 q

    #
    在某个元素之前或之后插入一个元素
    #linsert key before|after pivot element
    linsert list1 before q zz

    #
     获取指定索引的元素,如果使用负数,则从右边开始计算
    lindex list1 0
    • 1

    取值

    #弹出栈顶元素(从左边开始弹出,弹出之后,list中就不存在该元素了)
    #lpop key 
    lpop list1
    #弹出栈顶元素(从右边开始弹出,弹出之后,list中就不存在该元素了)
    #rpop key 
    rpop list1

    #
    按照索引位置取值
    #lindex key index
    lindex list1 0

    #
    取值,从头部到尾部
    #lrange key start end   (负下标表示的是从后往前,如-1表示倒数第一)
    lrange list1 0 -1
    1) "5"
    2) "4"
    3) "3"
    4) "2"
    5) "1"

    #
     blpop和brpop是阻塞版本
    # blpop key timeout
    # timeout表示 列表为空时会等3秒后返回,如果timeout=0,则会一直阻塞下去,直到有数据为止
    # 如果多个客户端对同一个键执行blpop,那么先执行的blpop命令的客户端可以获取到弹出的值
    blpop list1 3
    • 1

    列表长度

    llen list1
    • 1

    删除值

    #删除几个某个元素  count>0时,从左边开始删除;count=0时,删除所有值为element的元素;count<0时,从右边开始删除
    #lrem key count element
    lrem list1 1 "hi"

    #
    截取指定范围的值赋给key(范围是索引)
    #ltrim key start stop
    ltrim list1 0 0

    #
     从头部删除元素  出栈
    lpop list1
    # 从尾部删除元素
    rpop list1
    • 1

    出栈入栈

    #从源列表右出栈压入左目标列表,将一个列表移至另一个列表
    #rpoplpush source dest
    rpoplpush list2 list1
    • 1

    Set类型

    set类型是单值多value,与List的区别在于不可重复,且没有顺序,类似于HashSet,通过hash table实现的

    set除了基本的增删改查操作之外,还可以使用集合来取并集、交集、差集,可以用来实现好友推荐等功能

    编码格式
    编码对象
    OBJ_ENCODING_INTSET使用整数集合实现的集合对象
    OBJ_ENCODING_HT使用的hash table

    set有两种编码格式

    • intset 保存的元素全都是整数,且元素数量不超过512
    • hash table 保存的不是整数
    #根据该配置项来进行编码转换的
    set-max-intset-entries 512
    • 1
    源码结构
    intset结构

    整数集合

    typedef struct intset {
      // 每个整数的类型
        uint32_t encoding;
      // intset的长度
        uint32_t length;
      // 整数数组
        int8_t contents[];
    } intset;
    • 1
    操作命令

    设置值

    #设置值
    #sadd key member [member ...]
    sadd set1 vv vc vb vv

    • 1

    取值

    #取值,集合中的所有元素
    #smembers key
    smembers set1
    1) "vc"
    2) "vb"
    3) "vv"

    #
    判断该元素是否在set
    #sismember key member
    sismember set1 vv

    #
    获取随机的元素,不会删除元素(count表示获取的数量,默认为1)
    #srandmember key [count]
    srandmember set1 2
    • 1

    查看集合元素个数

    #scard key
    scard set1
    • 1

    删除

    #srem key member [member ...]
    srem set1 vb

    #
    随机出栈(默认数量为1)
    #spop key [count]
    spop set1
    • 1

    随机抽取

    #随机从集合中抽取2个(默认1个)
    #srandmember key [count]
    srandmember set1 2
    • 1
    #将源集合中的值移至目标集合
    #smove source dest member
    smove set1 set2 test
    • 1

    差集 交集 并集

     #差集 返回的是其他key与第一个key的差集,属于A且不属于B的(A - B)
     #sdiff key [key ...]
     sdiff set1 set2
     
     #
    交集  属于A且属于B的(A ∩ B)
     #sinter key [key ...]
     sinter set1 set2
     #并集  属于A或属于B的(A ∪ B)
     #sunion key [key ...]
     sunion set1 set2
    • 1

    Hash类型

    value是键值对,相当于HashMap,对于hash碰撞也是采用的HashMap的处理方式,数组+链表

    更适合存储对象,将一个对象存储在hash类型中会占用更少的内存,且可以更方便的存取整个对象

    编码格式
    编码对象
    OBJ_ENCODING_ZIPLIST使用ziplist
    OBJ_ENCODING_HT使用的hash table

    hash有两种编码格式,当键值对数据量比较小时,使用紧凑的数组格式来节省内存空间

    • ziplist 一开始存储使用的ziplist,但是当满足一定条件时会转换为hash table
    • hash table
    #根据该配置项来进行编码转换的
    # 当hash对象的键值对数量大于该值时使用OBJ_ENCODING_HT编码 
    hash-max-ziplist-entries 512
    # 当hash对象中的键值对存在键或值的长度大于该值时使用OBJ_ENCODING_HT编码 
    hash-max-ziplist-value 64
    • 1
    源码结构

    dict.h

    // 哈希表结构
    typedef struct dictht {
       // 哈希表数组
        dictEntry **table;
       // 哈希表大小
        unsigned long size;
       // 哈希表大小掩码,用于计算索引值
        unsigned long sizemask;
       // 哈希表已有节点数量
        unsigned long used;
    } dictht;
    // 哈希节点
    typedef struct dictEntry {
       // 键
        void *key;
       // 值
        union {
            void *val;
            uint64_t u64;
            int64_t s64;
            double d;
        } v;
       // 下一个哈希节点,链表,可以将多个哈希值相同的键值对进行连接,解决hash冲突的问题
        struct dictEntry *next;
    } dictEntry;
    // 字典结构
    typedef struct dict {
       // 类型
        dictType *type;
       // 私有数据
        void *privdata;
       // 哈希表
       // ht[0]:用来存放真实的数据
       // ht[1]:用于扩容
        dictht ht[2];
       //rehash 如果为-1表示没有进行rehash
        long rehashidx; /* rehashing not in progress if rehashidx == -1 */
        int16_t pauserehash; /* If >0 rehashing is paused (<0 indicates coding error) */
    } dict;


    // 处处不同的数据结构,使用不同的hash算法,不同的键值比较算法,不同的析构函数
    typedef struct dictType {
       // hash函数
        uint64_t (*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);
        int (*expandAllowed)(size_t moreMem, double usedRatio);
    } dictType;
    • 1
    哈希冲突

    redis哈希冲突是使用链地址法进行解决的,将新节点添加在链表的表头

    扩容

    redis的字典源码中的dictht是一个容量为2的数组,其作用就是用于进行扩容的

     // 哈希表
       // ht[0]:用来存放真实的数据
       // ht[1]:用于扩容
        dictht ht[2];
    • 1

    最初始的数据是存放在ht[0]中的,当要进行扩展或收缩时,ht[1]会根据ht[0]的容量来计算容量,ht[1]扩展的大小是比当前ht[0].used值的两倍大的第一个2的整数幂;收缩的大小是ht[0].used的第一个大于等于的2的整数幂。然后将ht[0]的所有键值重新散列到ht[1]中,重新计算所有的数组下标,当数据迁移完后ht[0]就会释放,然后将ht[1]改为ht[0],为下一次扩展或收缩做准备

    由于有时候redis中的数据比较多,不能瞬间完成扩容操作,会将rehash操作进行分步进行,其字段rehashidx就用于标识rehash过程,如果是-1表示当前没有进行rehash操作,当进行rehash操作时会将该值改为0,在进行更新、删除、查询操作时会在ht[0]和ht[1]中都进行,先更新ht[0],在更新ht[1],而进行新增操作就直接新增到ht[1]中,保证ht[0]只减不增,直到rehash完成

    操作命令

    设置值

    #Redis4.0之后可以设置多个值,与hmset功能相同
    #hset key field value [field value ...]
    # 不区分插入和更新,插入时返回1,更新时返回0
    hset user id 123

    #
    设置多个字段
    #hmset key field value [field value ...]
    hmset user name zhangsan age 18

    #
    不存在则设置值
    #hsetnx key field value
    hsetnx user id 12
    • 1

    获取值

    #hget key field
    hget user id
    #获取多个字段的值
    #hmget key field [field ...]
    hmget user id name sex age

    #
    可以获取该key所对应的map
    #hgetall key
    hgetall user

    #
    获取key对应的map有几个字段
    #hlen key
    hlen user

    #
    判断该key的某个字段是否存在
    #hexists key field
    hexists user id

    #
    获取该key下所有的字段
    #hkeys key
    hkeys user

    #
    获取该key下所有的字段值
    #hvals key 
    hvals user
    • 1

    删除字段值

    #支持删除多个字段
    #hdel key field [field ...]
    hdel user age
    • 1

    数值操作

    # 加2
    # hincrby key field increment
    hincrby user id 2

    #
     减2
    # hdecrby key field increment
    hdecrby user id 2
    • 1

    Zset类型

    zset是sorted set,即有序的集合,在set的基础上加了一个score,类似于TreeSet,使用score来进行排序,也可以根据score的范围来获取元素的列表

    原本set值为k1 v1 k2 v2,而zset是k1 score1 v1 k2 score2 v2

    底层使用的是dict哈希表和skiplist跳表,dict用来关联value和score,保证value的唯一性,同时通过value可以获取到score。skiplist的作用在于给value排序以及根据score的范围来获取元素列表

    编码格式
    编码对象
    OBJ_ENCODING_ZIPLIST使用ziplist
    OBJ_ENCODING_SKIPLIST使用的skiplist

    zset有两种编码格式

    • ziplist 满足条件使用ziplist,否则使用skiplist
    • skiplist
    #根据该配置项来进行编码转换的
    zset-max-ziplist-entries 128
    zset-max-ziplist-value 64
    • 1
    源码结构

    跳表skiplist相比一般的链表,有更高的查找效率,效率可以比拟二叉查找树

    跳表搜索
    跳表搜索

    在存储时会预先间隔地保存了有序链表的节点,从而在查找时能达到类似于二分搜索的效果,可以节省查找时间,但是浪费了空间,数据只存储了一次,只是指针多次存储了

    • 由多层结构组成
    • 每一层都是一个有序链表
    • 最底层的链表包含了所有元素
    • 如果一个元素出现在leveli的链表中,则它在leveli之下的链表也都会出现
    • 每个节点包含两个指针,一个指向同链表中的下一个元素,一个指向下面一层的元素
    typedef struct zset {
       // 哈希表
        dict *dict;
      // 跳表
        zskiplist *zsl;
    } zset;

    typedef struct zskiplistNode {
      // 节点数据
        sds ele;
      // 分数
        double score;
      // 后继指针
        struct zskiplistNode *backward;
      // 前驱指针
        struct zskiplistLevel {
            struct zskiplistNode *forward;
          // 调到下一个数据项需要走几步,计算rank时有用
            unsigned long span;
        } level[];
    } zskiplistNode;

    typedef struct zskiplist {
      // 跳表头尾指针
        struct zskiplistNode *header, *tail;
      // 跳表长度
        unsigned long length;
      // 跳表高度
        int level;
    } zskiplist;
    • 1
    操作命令

    设置值

    # 向zset中添加元素,并且会根据score进行排序,如果元素存在,会更新score
    #zadd key score member [score member ...]
    zadd zset1 1 q 2 w
    • 1

    取值

    #WITHSCORES表示查询结果是否带着score,score从小到大
    #zrange key start stop [WITHSCORES]
    zrange zset1 0 -1

    #
    WITHSCORES表示查询结果是否带着score,score从大到小
    #zrevrange key start stop [WITHSCORES]
    zrevrange zset1 0 -1

    #
    根据score范围查询
    #zrangebyscore key min max [WITHSCORES] [LIMIT offset count]
    zrangebyscore zset2 80 100

    #
    根据score的范围来计数
    #zcount key min max
     zcount zset2 0 10
     
     #
    根据值来获取下标(根据score从小到大来排的序)
     #zrank key member
     zrank zset2 d
     
     #
    根据值来获取下标(根据score从大到小来排的序)
     #zrevrank key member
     zrevrank zset2 d
     
     #
    获取元素的score
     #zscore key member
     zscore zset2 c
     
    • 1

    查看集合元素个数

    #该key的元素个数
    zcard zset2
    • 1

    删除

    #zrem key member
    zrem zset2 s

    #
     根据下标区间来删除(根据score从小到大来排的序)
    #zremrangebyrank key start stop
    zremrangebyrank zset1 5 8

    #
     根据score区间来删除
    # zremrangebyscore key min max
    zremrangebyscore zset1 7 10
    • 1

    数值操作

    # 如果key中存在该元素,则该元素的score增加increment,否则新增该元素
    # zincrby key increment member
    zincrby zset1 2 b
    • 1

    Bitmap类型

    位图不是一个真实的数据类型,是定义在字符串类型之上的面向位操作的集合,每个单元只能存储0和1。位图的最大优势在于节省空间

    127.0.0.1:6379> type bittest
    string
    • 1

    设置值

    #setbit key offset value
    # value只能是0或者1
    setbit bittest 10 1
    • 1

    取值

    #getbit key offset
    # 如果offset超出范围的话返回0
    getbit bittest 10

    #
    bitcount key [start] [end]
    #获取指定范围为1的个数
    bitcount bittest 0 1000
    • 1

    bitmap适用于大量数据,少量数据的话会导致大部分位都是0,占用的内存数比集合还多

    HyperLogLog类型

    HyperLogLog是一种基于字符串类型的基数算法。通过HyperLogLog可以利用极小的内存空间完成独立总数的统计

    127.0.0.1:6379> type hpl
    string
    • 1
    # 添加
    # pfadd key element [element ...]
    pfadd hpl 123

    #
     统计
    # pfcount key [key ...]
    pfcount hpl

    #
    合并
    # pfmerge destkey sourcekey [sourcekey ...]
    pfmerge destkey sourcekey1 sourcekey2
    • 1

    HyperLogLog非常的节省空间,但是HyperLogLog统计的数据是不准确的

    GEO类型

    地理信息定位功能,支持存储地理位置信息。其底层是zset

    127.0.0.1:6379> type key
    zset
    • 1
    # 增加地理位置信息
    # longitude经度
    # latitude纬度
    # member成员
    # geoadd key longitude latitude member [longitude latitude member ...]
    geoadd key 116.28 39.55 bj

    #
     获取地理位置信息 返回经纬度
    #geopos key member [member ...]
    geopos key bj

    #
     获取两个地理位置的距离 unit为单位 包含 m 米、km 千米、mi 英里、ft 尺
    # geodist key member1 member2 [unit]
    geodist key bj sjz km

    #
     获取指定位置范围内的地理信息集合
    # georadius key longitude latitude radius m|km|ft|mi [withcoord] [withdist] [withhash] [COUNT count] [asc|desc] [store key] [storedist key]
    # longitude latitude也可以使用成员来替换,此时使用georadiusbymember key member
    # radius m|km|ft|mi 表示半径+单位
    # withcoord 返回结果中包含经纬度
    # withdist 返回结果中包含里节点位置的距离
    # withhash 返回结果中包含geohash
    # COUNT count 指定返回的数量
    # asc|desc 按照距离做升序或者降序
    # store key 将返回结果的地理位置信息保存到指定键
    # storedist key 将返回结果距离节点距离保存到指定键
    georadiusbymember key bj 250 km

    #
     删除地理位置信息
    # zrem key member
    • 1

    各个类型的场景

    • String:

      • 缓存 set key value
      • 分布式锁 setnx key value
      • 限流/计数器 INCR count DECR count
      • 分布式id 使用INCR来生成id INCR id
      • pv/uv统计 可以使用incr来统计文章的访问量,key设置为post:文章ID:view
      • 分布式session
    • List:

      • 消息队列 LPUSH list item + brpop list来实现阻塞队列
      • 评论
      • 热点列表 如微博话题 LRANGE list 0 10

      lpush+lpop = 栈

      lpush+rpop = 队列

      lpush+ltrim = 有限集合

      lpush+brpop = 消息队列

    • Set: 点赞、标签

      • 标签 文章标签很多,每个人喜好也很多,推送的时候使用交集 SINTER set1 set2 set3
    • Hash: 存储对象

    • Zset: 排行榜

      • ZADD 添加元素
      • ZRANGE 按分数从低到高排列,返回区间内的元素
      • ZREVRANGE 按分数从高到低排列,返回区间内的元素
      • ZRANK 返回某个元素的排名
    • Bitmap

      • 可以用来做布隆过滤器
      • 可以用来统计每天的用户数
    • HyperLogLog

      • 可以用来计算独立总数,但是不可以获取用户id(而且数据存在一定的误差)
    • GEO

      • 可以用来做附近位置等依赖于地理位置的功能

    https://zhhll.icu/2021/数据库/非关系型数据库/redis/基础/7.redis数据结构/

    本文由 mdnice 多平台发布

  • 相关阅读:
    顾曼宁(顾曼):漂流伞创始人与杭州高层次人才的杰出代表
    数据中台方案分析和发展方向
    事务 @Transaction
    15 DOM 扩展
    测试脚本-通过csv文件调用脚本(可以按照num的顺序进行调用)-unittest
    网络安全(黑客)自学
    代码案例详解!如何让机器学习模型自解释!⛵
    如何构建完整的财务体系?
    Java并发编程学习八:ThreadLocal
    66从零开始学Java之集合中的Collection体系
  • 原文地址:https://blog.csdn.net/Lxn2zh/article/details/134524985