redis 数据类型和对象类型

Posted perfy576

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了redis 数据类型和对象类型相关的知识,希望对你有一定的参考价值。

1 数据类型

这些数据结构我们并不会用到,他是redis底层的数据结构.

我们能够用到的是对象

  

1.1 简单动态字符串

redis中的字符串使用的是

struct sdshdr
{
  int len; //已用长度
  int free; //未用长度
  char buf[]; // buf 的大小为len+free+1
}

  最后依然兼容c的字符串,最后一个字节使用‘\0‘,因此最终的总长度为len+free+1

相比较c的字符串优点在于:

  1. o(1)内获得字符串长度

  2. 避免了缓冲区溢出

  3. 减少了修改字符串带来的内存充分配次数

  4. 二进制安全,使用len记录长度,而不是\0

    因此字符串中可以出现‘\0‘

 

 

空间与分配

小于1M时,分配:需要长度×2+1?,因此最终len和free相同

大于1M是,多分配1M长度.也就是?1024*1024

且在字符串缩短的时候不会释放重新分配.

1.2 链表

使用的是一个双向链表.存储的是指针void*

 

 

typedef struct listNode
{
  struct listNode *prev;
  struct listNode *next;
  void *value;
}listNode;

  

然后,再次封装了上面的链表结构,可以实现多态.

typedef struct list
{
  listNode *head; //指向链表头
  listNode *tail; //指向链表尾
  unsigned long len; //链表总长
  void *(*dup)(void* ptr); //复制节点的函数
  void *(free)(void* ptr); //释放节点的函数
  int (*match)(void* ptr,void* key); //比较节点与一个值的函数
};

  

  1. 链表是双向的

  2. 无环

  3. 记录了头尾指针

  4. 记录了链表长度

  5. 多态

1.3 字典hash

distht结构存储一个哈希表,记录了哈希表总大小,掩码,已有节点数量

typedef struct dictht {
    dictEntry **table;      //哈希表数组
    unsigned long size;     //哈希表大小
    unsigned long sizemask; //用于计算索引值,
                            //总是等于 size - 1
    unsigned long used;     //哈希表已有节点数量
}  dictht;

  每个节点结构内部记录了,键,值.值使用联合体,是一个8字节大小的,因此可以存储一个指针.同时还有一个指向下一个节点的指针.

typedef struct dictEntry {
    void *key;          //键
    union {             //值
        void *val;
        uint_64 u64;
        int64_t s64;
    } v;                    
    sturct dictEntry *next; //指向下个哈希表节点,形成链表
} dictEntry;

  

字典使用链表法解决冲突

 dict结构进一步封装字典,能够主要是记录类型和方便rehash

typedef struct dict {
    dictType *type;     //类型特定函数
    void *privdata;     //私有数据
    dictht ht[2];       //哈希表,rehash的时候使用另一个
    int rehashdx;      //rehash 索引,当 rehash 不在进行时,值为-1
} dict;

  dictType用于实现多态

typedef struct dictType {  
    unsigned int (*hashFunction)(const void *key);// 含key的hash函数  
    void *(*keyDup)(void *privdata, const void *key);// key的拷贝函数  
    void *(*valDup)(void *privdata, const void *obj);// value的拷贝函数  
    int (*keyCompare)(void *privdata, const void *key1, const void *key2);  // 对key的compare函数  
    void (*keyDestructor)(void *privdata, void *key);// key析构  
    void (*valDestructor)(void *privdata, void *obj);// value析构  
} dictType;  

  

因此存储过程是

int index = dict->type->hashFunction(key) & dict->ht[x].sizemask;

  

redis的哈希算法使用的murmurhash

使用了链表发解决冲突

 

rehash过程

  1. 如果没有执行bgsave,也就是rdb持久化,那么负载银子大于等于1时执行

  2. 在执行bgsave,那么负载因子大于等于5时执行.

  3. 负载因子小于0.1时开始收缩

扩展的大小为上次使用的大小的2倍.然后取2^n,收缩的则是取已使用取2^n.都是取的最近接的一个2^n.

使用的是ht[2]里面的两个表相互对调的方式.然后再讲ht[2]两个互换,也就是最终,都是ht[0]中存储哈希表

  

渐进式rehash

  1. ht[1]分配空间

  2. rehashidx设置为0,表示rehash开始

  3. 随后的在增删改查过程,都会将对应的hash值的ht[0]中的数据,rehash到ht[1]

  4. 直到最后一个ht[0]中的节点被rehash

渐进式rehash的查增删改查会在两个表中依次进行一次.也就是ht[0]一直在减少.

 

1.4 跳跃表

使用跳跃表,.代替红黑树和平衡树来实现set.其实就是c++中的map,是一个有序键值对的集合

只在有序集合和集群节点时使用.

 

1.5 整数集合

就是个扩展的数组.

存储了数组中每个元素所占位数和长度,然后紧跟这一个数组.

数组中每个元素所占位数称为编码.

整数集合中的元素可以由低编码,转换为高编码,但是不能反过来转换,也就是从16位到32可以,反过来不可以.

这个过程称为升级,当天家一个多位的元素是,整个集合都需要升级.

过程为:

  1. 计算空间,并分配

  2. 从后向前一次搬运

1.6 压缩列表

是一个嗯...自定义的数组结构,用来存储整数和短字符串.

也就是说一个压缩列表里可以同时存储两种类型的数据.

压缩列表就是一个连续内存区域,头部记录

  1. 整个内存区域总长度4字节

  2. 最后一个节点到头部的距离.4字节

  3. 节点数量

后面根一个个的节点,最后一个特殊值表示结尾

每个节点的格式为:前一个节点的长度(采用1字节或是5字节来揭露),本节点的编码,编码为本节点的数据类型和长度.数据

连锁更新

值得是节点的前一节点长度字段的连锁更新.

当压缩列表中的所有节点都是250~254左右,也就是一个字节能够记录.然后头部插入一个大于254长度的节点,因此后一个节点本来长度字段使用1字节,因此变为5字节,连锁的后面的节点的长度字段都需要跟着更新,成为连锁更新.

最坏复杂度o(n^2)

 

2 对象类型

编码和对象

redis使用对象来表示数据库中的键和值.当在数据库中创建一个键值对的时候,至少就创建了两个对象:一个键,一个值

  1. 字符串对象

    整数值实现实现 int

    embstr的简单动态字符串embstr

    简单动态字符串实现raw

  2. 列表对象

    压缩列表实现

    双端链表实现linkedlist

  3. 哈希对象

    字典实现hashtable

    压缩列表实现ziplist

  4. 集合对象

    整数集合实现intset

    字典实现

  5. 有序集合对象

    压缩列表实现

    跳跃表和字典实现skiplist

2.1 字符串对象

浮点数被保存为字符串,计算的时候会先转为浮点数然后在计算,计算完了在以字符串的形式保存.

只有整数会被保存为int,

  1. set 新建

    整数被保存为int,其他被保存为字符串

  2. get 获取

    都转化为raw也就是简单动态字符串,然后返回字符串

    没有则nil

  3. append

    转换为raw.raw调用sdscatlen

    没有则直接新建

  4. incrbyfloat 基础上加一个浮点数

    只有int类型,可以其他两种报错

    没有则新建.

  5. incrby 整数假发

    同上

  6. decrby

    同上

  7. strlen

    转换为字符串,返回长度

  8. setrange name index value

    转换为字符串,改变指定位置上的值

  9. getrange name start end

    转换为字符串,返回给定两个索引作为起始坐标的字符串

2.2 列表对象

ziplist和linkedlist

ziplist的使用条件为

  1. 每个字符串元素长度小于64

  2. 元素个数小于512

否则会被转化.redis自动转化

 

  1. lpush

    压入链表头

  2. rpush

    尾部添加

  3. lpop

    头删除

  4. rpop

    尾删除

  5. lindex name index

    返回指定index的元素

  6. llen

    链表长

  7. linsert name BEFORE/AFTER 已存在值 要加入的值

    指定位置插入,指定的已存在的值,而不是index

  8. lrem

    不懂

  9. lset name index value

    替换,指定的index

 

2.3 哈希对象

ziplish或是hashtable

转换条件同上

操作

命令作用
hset obj key value 插入值
hget obj key 获取值,不存在返回nil
hexosts obj key 是否存在,不存在返回0
hdel obj key 删除键值对
hlen obj 获取元素个数
hgetall obj

获取所有

 

2.4 集合对象

intset或是hashtable

转换条件

  1. 存在元素不是整数值

  2. 保存元素超过512

 

 

 

命令描述
sadd obj ... 添加元素
scard obj 返回元素数量
sismember obj value 是否存在,没有返回0
srandmember obj 随机返回一个数
spop obj 随机返回并删除
srem obj value 删除给顶元素
   

2.5 有序集合

ziplist和skiplish

有序集合使用字典和跳跃表实现的时候,是为了增加性能.集合了两种性能.同事保存,也就是保存了两份数据,一份用哈希表一份用跳跃表

转换条件:

  1. 元素长度大于64

  2. 元素个数大于128

命令描述
zadd obj number value 插入,key只能是数值,float
zcard obj 返回数量
zcount obj value1 value2 给定value 范围内的数量,value只能是数值,float
zrange obj index1 index2 返回范围内的所有的值
zrevramge 反向饭饭
zrank obj value 返回value所对应key的排名
zrevrank 反向,同上
zrem value 删除执行
score obj value

返回对应的key

 

内存回收

redis实现了引用计数的内存回收.

 

共享对象

服务器初始化穿件了0~9999的一万个字符串,共享这些对象.

使用

OBJECT REFCOUNT name查看计数,一般只是上面的10000个整数共享了.

对象空转时长

也就是记录从上次访问到现在经过的时间.单位秒

OBJECT IDELTIME name

 

 

以上是关于redis 数据类型和对象类型的主要内容,如果未能解决你的问题,请参考以下文章

Redis 学习 —— 数据类型及操作

redis 数据类型和对象类型

redis 系列15 数据对象的(类型检查,内存回收,对象共享)和数据库切换

Redis 详解 redis的五大数据类型实现原理

Redis 基础 -- Redis数据类型之hashredis原则string存储对象和hash存储对象对比

一文读懂Redis常见对象类型的底层数据结构