[每天进步一点点]Redis笔记:常用的基本数据类型

2020-04-11  本文已影响0人  maomaov5

Redis常用的基本数据类型

激励:人人都有一个大厂的心,坚持自己的梦想,你就是世界。

乏味:笔记很无聊,需要去品味。

坚持:每天进步一点点,当知道的越多,才发现不知道的也越多。

String

最基本也是最常用的数据类型,也被叫做Binary-safe strings

  • 可以用来存储字符串、正数、浮点数。

操作命令

实现原理

结构图

备注:SDS:Simple Dynamic String,redis中字符串的实现。

数据模型

Redis是KV形式的数据库,它是通过hashtable实现的,所以每个键值对都有一个dictEntry(源码参考:dict.h)。

typedef struct dictEntry {
    void *key; /* key 关键字定义 */
    union {
        void *val;
        uint64_t u64; /* value 定义 */
        int64_t s64;
        double d;
    } v;
    struct dictEntry *next; /* 指向下一个键值对节点 */
} dictEntry;    
内部编码

应用场景

Hash哈希

操作命令

实现原理

结构示例图
存储类型

包含键值的无序散列表,val只能是字符串且不能嵌套其他类型。

存储原理

redis的hash本身也是一个kv的结构,类似于java中的HashMap。

外层的哈希(redis kv的实现)只用到了hashtable,当存储hash数据类型时,一般叫做内层的哈希。

内层的哈希底层可以使用两种数据结构实现:ziplist:OBJ_ENCODING_ZIPLIST(压缩列表),hashtable:OBJ_ENCODING_HT(哈希表)。

  • ziplist压缩列表: 是一个经过特殊编码的双向链表,它不存储指向上一个链表节点和指向下一个链表节点的指针,而是存储上一个节点长度和当前节点长度,通过牺牲部分读写性能来换取高效的内存空间利用率,是一种时间换空间的思想。只用在字段个数少、字段值小的场景。当hash对象同时满足以下两个条件的时候,使用ziplist编码:
  1. 所有的键值对的键和值的字符串长度都小于等于64byte(一个英文字母一个字节);
  2. 哈希对象保存的键值对数量小于512个;
// src/redis.conf配置
hash-max-ziplist-value 64  //ziplist中最大能存放的值长度
hash-max-ziplist-entries 512 //ziplist中最多能存放的entry节点数量

一个哈希对象超过配置的阈值(键和值的长度大于64byte,键值对个数大于512个)时,会转换成哈希表hashtable。

  • hashtable(dict):hashtable被称为dictionary,它是一个数组+链表的结构。

redis的hash默认使用的是ht[0],ht[1]不会初始化和分配空间。

哈希表dictht是用链地址法来解决碰撞的问题,在这种情下,哈希表的性能取决于它的大小(size属性)和它所保存的节点的数量(used属性)之间的比率;

  • 比率在1:1时(一个哈希表ht只存储一个节点entry),哈希表的性能最好;
  • 如果节点数量比哈希表的大小要大很多的话(比例用ratio表示,5表示平均一个ht存储5个entry),那么哈希表就会退化成个多个链表,哈希表本身的性能优势就不存在了。在这个情况下需要扩容。redis里面的这种操作叫做rehash。
  • rehash的步骤:
    1. 为字符ht[1]哈希表分配空间,这个哈希表的空间大小取决于要执行的操作和ht[0]当前包含的键值对的数量。(ht[1]的大小为第一个大于等于ht[0].used*2);
    2. 将所有的ht[0]上的节点rehash到ht[1]上,重新计算hash值和索引,然后放到指定的位置;
    3. 当ht[0]全部迁移到ht[1]后,释放ht[0]的空间,将ht[1]设置为ht[0]表,并创建新的ht[1]为下次rehash做准备。

应用场景

List列表

操作命令

存储类型

存储有序的字符串(从左到右),元素可以重复。可以当简单的队列和栈使用.

结构图
存储原理

3.2版本之前,数据量较小的时候用ziplist存储,达到阈值时转换为linkedlist进行存储,分别对应OBJ_ENCODING_ZIPLIST和OBJ_ENCODING_LINKEDLIST。

3.2版本之后统一使用了quicklist来存储。quicklist存储了一个双向链表,每个节点都是一个ziplist。

  • quicklist

    • quicklist(快速列表)是ziplist和linkedlist的结合体。
    • quicklist.h,head和tail指向双向链表的表头和表尾。
    typedef struct quicklist {
        quicklistNode *head; /* 指向双向列表的表头 */
        quicklistNode *tail; /* 指向双向列表的表尾 */
        unsigned long count;
        /* 所有的 ziplist 中一共存了多少个元素 */
        unsigned long len;
        /* 双向链表的长度,node 的数量 */
        int fill : 16;
        /* fill factor for individual nodes */
        unsigned int compress : 16; /* 压缩深度,0:不压缩; */
    } quicklist;
    

    配置参数(redis.conf):

    • List-max-ziplist-size(fill):正数表示单个ziplist最多包含的entry个数。负数代表单个ziplist的大小,默认8k。(-1:4KB;-2:8KB;-3:16KB;-4:32KB;-5:64KB)
    • List-compress-depth(compress):压缩深度,默认是0。(1:首尾的ziplist不压缩;2:首尾第一第二个ziplist不压缩,以此类推)

应用场景

Set集合

操作命令

存储类型

String类型的无序集合,最大存储数量为2^32 - 1。

结构图
存储原理

redis用intset或hashtable来存储set。如果元素都是整数类型,就用intset存储,如果不是整数类型,就用hashtable存储,如果元素个数超过512个,也会用hashtable存储。

应用场景

ZSet有序集合

操作命令

存储类型

sorted set:有序的set,每个元素都有一个score。如果score相同,按照key的ASCII码排序。

结构图
存储原理

同时满足以下条件使用ziplist:

  1. 元素个数小于128;
  2. 所有member的长度都小于64字节;

在ziplist的内部,按照score排序递增来存储,插入的时候要移动之后的数据。超过阈值之后使用skiplist+dict存储。

假设我们要查找22这个值,

  1. 22首先和5比较,然后再和17比较,22比它们都大,所以继续向后比较;
  2. 当22和27比较时,发现22小于27,则会进入下面的链表,开始比较;
  3. 22正好时下一个链表的值,这样就顺利找到了。

假如要查找25,根据上边流程第三步发现25比22大,会继续向后查询,然后又比27小,说明要查询的25在原链表中不存在。

在整个查询过程中,由于新增加了指针,查询的时候就不需要遍历整个链表的节点,这样查询的次数大概减少了一半,这个就叫做跳跃表。

应用场景

BitMaps

BitMaps时在字符串类型上面定义的位操作,一个字节又8个二进制位组成。

操作命令

应用场景

其他数据类型


本文由博客一文多发平台 OpenWrite 发布!

上一篇 下一篇

猜你喜欢

热点阅读