Redis学习笔记(一):数据结构与对象

本文内容摘自《redis设计与实现》黄建宏

1. 数据结构

redis主要基于以下数据结构实现:

  • 简单动态字符串(SDS)
  • 双端链表
  • 字典
  • 跳跃表
  • 压缩列表
  • 整数集合

2. 对象系统

redis基于上述数据结构构建了一个对象系统,针对不同的使用场景,为对象设置多种不同的数据结构实现,包括:

  • 字符串对象 (string)
  • 列表对象(list)
  • 哈希对象(hash)
  • 集合对象(set)
  • 有序集合对象(zset)

redis使用键值对保存数据,键和值分别是对象,并且键总是字符串对象,值是上述5中对象之一,因此当称一个数据库键为列表键指的是其值为列表对象。

2.1 对象底层数据结构实现

对象的ptr指针指向底层的数据结构,由encoding属性决定使用哪种数据结构,每种类型的对象都至少使用了两种encoding,可以用object encoding key查看;通过encoding属性来设定对象所使用的编码,而不是为特定类型的对象关联一种固定的编码,极大地提升了Redis的灵活性和效率,因为Redis可以根据不同的使用场景来为一个对象设置不同的编码,从而优化对象在某一场景下的效率。

数据结构encodingobject encoding命令输出
整数REDIS_ENCODING_INT“int”
emstr编码的简单动态字符串REDIS_ENCODING_EMBSTR“embstr”
简单动态字符串REDIS_ENCODING_RAW“raw”
字典REDIS_ENCODING_HT“hashtable”
双端链表REDIS_ENCODING_LINKEDLIST"“linkedlist”
压缩列表REDIS_ENCODING_ZIPLIST“ziplist”
整数集合REDIS_ENCODING_INTSET"“intset”
跳跃表和字典REDIS_ENCODING_SKIPLIST“skiplist”

2.2 字符串对象

2.2.1 底层实现

字符串对象的编码可以是int、raw、embstr。

  1. 如果一个字符串对象保存的是整数值,并且这个整数值可以用long类型来表示,那么字符串对象会将整数值保存在字符串对象结构的ptr属性里面(将void*转换成long),并将字符串对象的编码设置为int。
    整型字符串
  2. 如果字符串对象保存的是一个字符串值,并且这个字符串值的长度大于39字节(3.2之前的版本),那么字符串对象将使用一个简单动态字符串(SDS)来保存这个字符串值,并将对象的编码设置为raw在这里插入图片描述
  3. 如果字符串对象保存的是一个字符串值,并且这个字符串值的长度(包括最后的结束符)小于等于39字节,那么字符串对象将使用embstr编码的方式来保存这个字符串值。embstr编码是专门用于保存短字符串的一种优化编码方式,这种编码和raw编码一样,都使用redisObject结构和sdshdr结构来表示字符串对象,但raw编码会调用两次内存分配函数来分别创建redisObject结构和sdshdr结构,而embstr编码则通过调用一次内存分配函数来分配一块连续的空间,空间中依次包含redisObject和sdshdr两个结构
2.2.2 编码转换

int编码的字符串对象和embstr编码的字符串对象在条件满足的情况下,会被转换为raw编码的字符串对象:

  • 对于int编码的字符串对象来说,如果我们向对象执行了一些命令,使得这个对象保存的不再是整数值,而是一个字符串值,那么字符串对象的编码将从int变为raw。
  • 另外,因为Redis没有为embstr编码的字符串对象编写任何相应的修改程序(只有int编码的字符串对象和raw编码的字符串对象有这些程序),所以embstr编码的字符串对象实际上是只读的。当我们对embstr编码的字符串对象执行任何修改命令时,程序会先将对象的编码从embstr转换成raw,然后再执行修改命令。因为这个原因,embstr编码的字符串对象在执行修改命令之后,总会变成一个raw编码的字符串对象。
2.2.3 字符串键常用命令
命令用途
SET key value插入字符串键值
GET key获取key对应的值
APPEND key new将new追加到vaule
INCRBY key number加法,只有int编码才能用
DECRBY key number减法,只有int编码才能用
STRLEN key返回字符串的长度
SETRANGE key startindex value将startindex 开始的值覆盖为给定的字符
GETRANGE key start end返回字符串索引上的值

2.3 列表对象(list)

列表对象的编码可以是ziplist或者linkedlist,当列表对象保存的所有字符串元素的长度都小于64字节且列表对象保存的元素数量小于512个时使用跳跃表作为底层实现。

命令用途
LPUSH将元素推入表头
RPUSH将元素推入表尾
LPOP将表头结点返回并移除
RPOP将表尾结点返回并移除
LINDEX返回指定位置元素
LINSERT将元素插入到指定位置

2.4 哈希对象(map)

哈希对象的编码可以是ziplist或者hashtable。ziplist编码的哈希对象使用压缩列表作为底层实现,每当有新的键值对要加入到哈希对象时,程序会先将保存了键的压缩列表节点推入到压缩列表表尾,然后再将保存了值的压缩列表节点推入到压缩列表表尾,因此:

  • 保存了同一键值对的两个节点总是紧挨在一起,保存键的节点在前,保存值的节点在后;
  • 先添加到哈希对象中的键值对会被放在压缩列表的表头方向,而后来添加到哈希对象中的键值对会被放在压缩列表的表尾方向。

当哈希对象可以同时满足以下两个条件时,哈希对象使用ziplist编码:

  • 哈希对象保存的所有键值对的键和值的字符串长度都小于64字节;
  • 哈希对象保存的键值对数量小于512个
命令用途
HSET添加键值对
HGET根据key获取value
HEXISTS判断key是否存在
HDEL删除key-value
HLEN返回整个哈希对象包含的键值对数量
HGETALL返回整个哈希对象的键值对

2.5 集合对象(set)

集合对象的编码可以是intset或者hashtable。intset编码的集合对象使用整数集合作为底层实现,集合对象包含的所有元素都被保存在整数集合里面。另一方面,hashtable编码的集合对象使用字典作为底层实现,字典的每个键都是一个字符串对象,每个字符串对象包含了一个集合元素,而字典的值则全部被设置为NULL。

当集合对象可以同时满足以下两个条件时,对象使用intset编码:

  • 集合对象保存的所有元素都是整数值;
  • 集合对象保存的元素数量不超过512个。
命令用途
SADD添加元素
SCARD返回集合数量
SISMEMBER判断元素是否存在
SMEMBERS获取所有元素
SREM删除指定元素
SPOP随机pop一个元素

2.6 有序集合对象(sorted_set)

有序集合的编码可以是ziplist或者skiplist。有序集合键的值为哈希对象,每个值维护一个member和score,member是键,score是值,根据这个score排序。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值