Redis数据结构_对象

Redis用到的所有主要数据结构,简单动态字符串(SDS)、双端链表、字典、压缩列表、整数集合、跳跃表。

Redis并没有直接使用这些数据结构来实现键值对数据库,而是基于这些数据结构创建了一个对象系统,这个系统包含字符串对象、列表对象、哈希对象、集合对象和有序集合对象这五种类型的对象,而每种对象又通过不同地编码映射到不同的底层数据结构。

Redis中的每个对象都由一个redisObject结构表示,该结构中和保存数据有关的三个属性分别是type属性、 encoding属性和ptr属性:

Redis使用对象来表示数据库中的键和值,每次当我们在Redis的数据库中新创建一个键值对时,我们至少会创建两个对象,一个对象用作键值对的健(键对象),另一个对象用作键值对的值(值对象)。

typedef struct redisObiect{
	//类型
	unsigned type:4;
	//编码
	unsigned encoding:4;
	//指向底层数据结构的指针
	void *ptr; 
}

其中Redis的键对象都是字符串对象,而Redis的值对象主要有字符串、哈希、列表、集合、有序集合几种。其分别对应的内部编码和底层数据结构如下图所示:

在这里插入图片描述

一、字符串(String)

字符串对象是 Redis 中最基本的数据类型,也是我们工作中最常用的数据类型。redis中的键都是字符串对象,而且其他几种数据结构都是在字符串对象基础上构建的。字符串对象的值实际可以是字符串、数字、甚至是二进制,最大不能超过512MB

内部实现

Redis字符串对象底层的数据结构实现主要是int和简单动态字符串SDS,其通过intrawembstr三种不同的编码方式映射到不同的数据结构。Redis会根据当前值的类型和长度来决定使用哪种编码来实现。

1.如果一个字符串对象保存的是整数值,并且这个整数值可以用long类型来表示,那么字符串对象会将整数值保存在字符串对象结构的ptr属性里面(将void*转换成long),并将字符串对象的编码设置为int

在这里插入图片描述

2.如果字符串对象保存的是一个字符串值,并且这个字符串值的长度大于32字节,那么字符串对象将使用一个简单动态字符串(SDS)来保存这个字符串值,并将对象的编码设置为raw。

在这里插入图片描述

3.如果字符串对象保存的是一个字符串值,并且这个字符申值的长度小于等于32字节,那么字符串对象将使用一个简单动态字符串(SDS)来保存这个字符串值,并将对象的编码设置为embstr

在这里插入图片描述

embstr编码是专门用于保存短字符串的一种优化编码方式,我们可以看到embstr和raw编码都会使用SDS来保存值,但不同之处在于embstr会通过一次内存分配函数来分配一块连续的内存空间来保存redisObject和SDS。而raw编码会通过调用两次内存分配函数来分别分配两块空间来保存redisObject和SDS。Redis这样做会有很多好处。

  • embstr编码将创建字符串对象所需的内存分配次数从raw编码的两次降低为一次

  • 释放 embstr编码的字符串对象同样只需要调用一次内存释放函数

  • 因为embstr编码的字符串对象的所有数据都保存在一块连续的内存里面可以更好的利用CPU缓存提升性能。

常用命令
命令描述时间复杂度
set key value [ex seconds] [px milliseconds] [nx xx]设置值O(1)
get key获取值O(1)
del key [key …]删除keyO(N)(N是键的个数)
mset key [key value …]批量设置值O(N)(N是键的个数)
mget key [key …]批量获取值O(N)(N是键的个数)
incr key将 key 中储存的数字值增一O(1)
decr key将 key 中储存的数字值减一O(1)
incrby key increment将 key 所储存的值加上给定的增量值(increment)O(1)
decrby key incrementkey 所储存的值减去给定的减量值(decrement)O(1)
incrbyfloat key increment将 key 所储存的值加上给定的浮点增量值(increment)O(1)
append key value如果 key 已经存在并且是一个字符串, APPEND 命令将指定的 value 追加到该 key 原来值(value)的末尾O(1)
strlen key返回 key 所储存的字符串值的长度。O(1)
setrange key offset value用 value 参数覆写给定 key 所储存的字符串值,从偏移量 offset 开始O(1)
getrange key start end返回 key 中字符串值的子字符O(N)(N是字符串的长度)

二、哈希(Hash)

在redis中,哈希类型是指Redis键值对中的值本身又是一个键值对结构,形如value=[{field1,value1},…{fieldN,valueN}]。

内部实现

哈希类型的内部编码有两种:ziplist(压缩列表),hashtable(哈希表)。只有当存储的数据量比较小的情况下,Redis 才使用压缩列表来实现字典类型。具体需要满足两个条件:

  • 当哈希类型元素个数小于hash-max-ziplist-entries配置(默认512个)

  • 所有值都小于hash-max-ziplist-value配置(默认64字节)

ziplist使用更加紧凑的结构实现多个元素的连续存储,所以在节省内存方面比hashtable更加优秀。当哈希类型无法满足ziplist的条件时,Redis会使用hashtable作为哈希的内部实现,因为此时ziplist的读写效率会下降,而hashtable的读写时间复杂度为O(1)。

有关ziplist的底层数据结构的具体实现参考:Redis数据结构——压缩列表

常用命令
命令说明时间复杂度
HDEL key field [field …]删除一个或多个Hash的fieldO(N) N是被删除的字段数量。
HEXISTS key field判断field是否存在于hash中O(1)
HGET key field获取hash中field的值O(1)
HGETALL key从hash中读取全部的域和值O(N) N是Hash的长度
HINCRBY key field increment将hash中指定域的值增加给定的数字O(1)
HINCRBYFLOAT key field increment将hash中指定域的值增加给定的浮点数O(1)
HKEYS key获取hash的所有字段O(N) N是Hash的长度
HLEN key获取hash里所有字段的数量O(1)
HMGET key field [field …]获取hash里面指定字段的值O(N) N是请求的字段数
HMSET key field value [field value …]设置hash字段值O(N) N是设置的字段数
HSET key field value设置hash里面一个字段的值O(1)
HSETNX key field value设置hash的一个字段,只有当这个字段不存在时有效O(1)
HSTRLEN key field获取hash里面指定field的长度O(1)
HVALS key获得hash的所有值O(N) N是Hash的长度
HSCAN key cursor [MATCH pattern] [COUNT count]迭代hash里面的元素

三、列表(List)

列表(list)类型是用来存储多个有序的字符串,列表中的每个字符串称为元素(element),一个列表最多可以存储232-1个元素。在Redis中,可以对列表两端插入(push)和弹出(pop),还可以获取指定范围的元素列表、获取指定索引下标的元素等。列表是一种比较灵活的数据结构,它可以充当栈和队列的角色,在实际开发上有很多应用场景。

在这里插入图片描述

列表类型有两个特点:

-列表中的元素是有序的,这就意味着可以通过索引下标获取某个元素或者某个范围内的元素列表。

-列表中的元素可以是重复的.

内部实现

在Redis3.2版本以前列表类型的内部编码有两种。

  • ziplist(压缩列表):当列表的元素个数小于list-max-ziplist-entries配置(默认512个),同时列表中每个元素的值都小于list-max-ziplist-value配置时(默认64字节),Redis会选用ziplist来作为列表的内部实现来减少内存的使用。

  • linkedlist(链表):当列表类型无法满足ziplist的条件时,Redis会使用linkedlist作为列表的内部实现。

而在Redis3.2版本开始对列表数据结构进行了改造,使用 quicklist 代替了 ziplist 和 linkedlist.

有关quicklist的底层数据结构的具体实现参考: Redis数据结构——quicklist

常用命令
命令说明时间复杂度
BLPOP key [key …] timeout删除,并获得该列表中的第一元素,或阻塞,直到有一个可用O(1)
BRPOP key [key …] timeout删除,并获得该列表中的最后一个元素,或阻塞,直到有一个可用O(1)
BRPOPLPUSH source destination timeout弹出一个列表的值,将它推到另一个列表,并返回它;或阻塞,直到有一个可用O(1)
LINDEX key index获取一个元素,通过其索引列表O(N)
LINSERT key BEFORE AFTER pivot value在列表中的另一个元素之前或之后插入一个元素O(N)
LLEN key获得队列(List)的长度O(1)
LPOP key从队列的左边出队一个元素O(1)
LPUSH key value [value …]从队列的左边入队一个或多个元素O(1)
LPUSHX key value当队列存在时,从队到左边入队一个元素O(1)
LRANGE key start stop从列表中获取指定返回的元素O(S+N)
LREM key count value从列表中删除元素O(N)
LSET key index value设置队列里面一个元素的值O(N)
LTRIM key start stop修剪到指定范围内的清单O(N)
RPOP key从队列的右边出队一个元O(1)
RPOPLPUSH source destination删除列表中的最后一个元素,将其追加到另一个列表O(1)
RPUSH key value [value …]从队列的右边入队一个元素O(1)
RPUSHX key value从队列的右边入队一个元素,仅队列存在时有效O(1)

四、集合

集合类型 (Set) 是一个无序并唯一的键值集合。它的存储顺序不会按照插入的先后顺序进行存储。

集合类型和列表类型的区别如下:

  • 列表可以存储重复元素,集合只能存储非重复元素;

  • 列表是按照元素的先后顺序存储元素的,而集合则是无序方式存储元素的。

一个集合最多可以存储232-1个元素。Redis除了支持集合内的增删改查,同时还支持多个集合取交集、并集、差集,合理地使用好集合类型,能在实际开发中解决很多实际问题。

内部实现

集合类型的内部编码有两种:

  • intset(整数集合):当集合中的元素都是整数且元素个数小于set-maxintset-entries配置(默认512个)时,Redis会选用intset来作为集合的内部实现,从而减少内存的使用。

  • hashtable(哈希表):当集合类型无法满足intset的条件时,Redis会使用hashtable作为集合的内部实现。

常用命令
命令说明时间复杂度
SADD key member [member …]添加一个或者多个元素到集合(set)里O(N)
SCARD key获取集合里面的元素数量O(1)
SDIFF key [key …]获得队列不存在的元素O(N)
SDIFFSTORE destination key [key …]获得队列不存在的元素,并存储在一个关键的结果集O(N)
SINTER key [key …]获得两个集合的交集O(N*M)
SINTERSTORE destination key [key …]获得两个集合的交集,并存储在一个关键的结果集O(N*M)
SISMEMBER key member确定一个给定的值是一个集合的成员O(1)
SMEMBERS key获取集合里面的所有元素O(N)
SMOVE source destination member移动集合里面的一个元素到另一个集合O(1)
SPOP key [count]删除并获取一个集合里面的元素O(1)
SRANDMEMBER key [count]从集合里面随机获取一个元素
SREM key member [member …]从集合里删除一个或多个元素O(N)
SUNION key [key …]添加多个set元素O(N)
SUNIONSTORE destination key [key …]合并set元素,并将结果存入新的set里面O(N)
SSCAN key cursor [MATCH pattern] [COUNT count]迭代set里面的元素O(1)

五、有序集合(ZSet)

有序集合类型 (Sorted Set或ZSet) 相比于集合类型多了一个排序属性 score(分值),对于有序集合 ZSet 来说,每个存储元素相当于有两个值组成的,一个是有序结合的元素值,一个是排序值。有序集合保留了集合不能有重复成员的特性(分值可以重复),但不同的是,有序集合中的元素可以排序。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-QuUZtMwF-1666147245780)(…/static/有序集合.png)]

内部实现

有序集合是由 ziplist (压缩列表) 或 skiplist (跳跃表) 组成的。当数据比较少时,有序集合使用的是 ziplist 存储的,有序集合使用 ziplist 格式存储必须满足以下两个条件:

  • 有序集合保存的元素个数要小于 128 个;

  • 有序集合保存的所有元素成员的长度都必须小于 64 字节。

如果不能满足以上两个条件中的任意一个,有序集合将会使用 skiplist 结构进行存储。

常用命令
命令说明时间复杂度
BZPOPMAX key [key …] timeout从一个或多个排序集中删除并返回得分最高的成员,或阻塞,直到其中一个可用为止O(log(N))
BZPOPMIN key [key …] timeout从一个或多个排序集中删除并返回得分最低的成员,或阻塞,直到其中一个可用为止O(log(N))
ZADD key [NXXX] [CH] [INCR] score member [score member …]添加到有序set的一个或多个成员,或更新的分数,如果它已经存在O(log(N))
ZCARD key获取一个排序的集合中的成员数量O(1)
ZCOUNT key min max返回分数范围内的成员数量O(log(N))
ZINCRBY key increment member增量的一名成员在排序设置的评分O(log(N))
ZINTERSTORE相交多个排序集,导致排序的设置存储在一个新的关键O(NK)+O(Mlog(M))
ZLEXCOUNT key min max返回成员之间的成员数量O(log(N))
ZPOPMAX key [count]删除并返回排序集中得分最高的成员O(log(N)*M)
ZPOPMIN key [count]删除并返回排序集中得分最低的成员O(log(N)*M)
ZRANGE key start stop [WITHSCORES]根据指定的index返回,返回sorted set的成员列表O(log(N)+M)
ZRANGEBYLEX key min max [LIMIT offset count]返回指定成员区间内的成员,按字典正序排列, 分数必须相同。O(log(N)+M)
ZREVRANGEBYLEX key max min [LIMIT offset count]返回指定成员区间内的成员,按字典倒序排列, 分数必须相同O(log(N)+M)
ZRANGEBYSCORE key min max [WITHSCORES] [LIMIT offset count]返回有序集合中指定分数区间内的成员,分数由低到高排序。O(log(N)+M)
ZRANK key member确定在排序集合成员的索引O(log(N))
ZREM key member [member …]从排序的集合中删除一个或多个成员O(M*log(N))
ZREMRANGEBYLEX key min max删除名称按字典由低到高排序成员之间所有成员。O(log(N)+M)
ZREMRANGEBYRANK key start stop在排序设置的所有成员在给定的索引中删除O(log(N)+M)
ZREMRANGEBYSCORE key min max删除一个排序的设置在给定的分数所有成员O(log(N)+M)
ZREVRANGE key start stop [WITHSCORES]在排序的设置返回的成员范围,通过索引,下令从分数高到低O(log(N)+M)
ZREVRANGEBYSCORE key max min [WITHSCORES] [LIMIT offset count]返回有序集合中指定分数区间内的成员,分数由高到低排序。O(log(N)+M)
ZREVRANK key member确定指数在排序集的成员,下令从分数高到低O(log(N))
ZSCORE key member获取成员在排序设置相关的比分O(1)
ZUNIONSTORE添加多个排序集和导致排序的设置存储在一个新的关键O(N)+O(M log(M))
ZSCAN key cursor [MATCH pattern] [COUNT count]迭代sorted sets里面的元素O(1)
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值