Redis Stream数据结构(1)

Redis5.0中的流数据结构:优化消息队列实现
Redis的流数据结构结合了列表、有序集合和发布订阅的优点,适合实现消息队列。它支持通过XADD命令添加元素,具有自动生成ID和限制长度的功能。XREAD命令允许阻塞式读取,优化了数据获取。此外,XTRIM和XDEL用于管理流的大小和删除元素,XRANGE和XREVRANGE则提供了访问流元素的机制。

Redis中的流数据结构

流(stream)是Redis5.0版本新添加的数据结构。在以往版本中,为了使用redis实现消息队列这一常见应用,一般使用列表、有序结合和发布订阅三种功能。但是这些实现存在着各自的弊端:

  • 列表实现的消息队列虽然可以快速地将消息追加到列表地末尾,但因为列表为线性结构,所以程序想要查找包含指定数据地元素,或者进行范围查找,就需要遍历整个列表。
  • 有序集合虽然可以有效地进行范围查找,但缺少列表和发布于订阅提供地阻塞弹出命令,使得无法使用有序集合去实现可阻塞消息弹出操作。
  • 发布订阅虽然拥有将消息传递给多个客户端地能力,并且也拥有相应地阻塞弹出原语,但发布订阅地“发后即忘”策略导致离线地客户端丢失消息。无法实现可靠地消息队列。
  • 三种数据结构都有一个问题,就是它们地元素只能是单个值,如果想要这些数据结构实现消息队列传递多项信息,就必须使用JSON之类地序列化格式将多项信息打包存储到单个元素上。

Stream流是上述三种数据结构地综合体,具备它们各自地有点以及特点,是使用Redis实现消息队列地最佳选择。流是一个包含零个或任意多个流元素地有序队列,队列中每个元素都包含一个ID和任意多个键值对,这些元素会根据ID地大小在流中有序进行排列。

常见命令

XADD 追加新元素到流的末尾

XADD命令,将一个带有指定ID以及包含指定键值对的元素追加到流的末尾。如果给定的流不存在,Redis会创建一个空白的流,然后将给定的元素追加到流中。

XADD stream id field value [field value ...]

流中每个元素可以包含一个或任意多个键值对,并且同一个流中的不同元素可以包含不同数量的键值对,比如其中一个元素可以包含3个键值对、另一个有5个键值对。

流元素会以有序方式存储用户给定的键值对:用户创建元素时以什么顺序给定键值对,它们被取出的时候就是什么顺序。

流元素的ID

流元素的ID由 毫秒时间 和顺序编号 两部分组成,其中使用UNIX时间戳表示的毫秒实标识元素和时间的关系,而以0为起始值的顺序编号用于区分同一时间内产生的多个不同元素。毫秒
时间和顺序编号都使用64位非负整数表示,所以整个流ID的总长为128位。使用"-"进行分割。

xadd stream 11100000000000-12345 k1 v1
#返回值为刚才指定的ID

在输入流ID的时候,除了可以给出带有毫秒时间和顺序编号的完整流ID之外,还可以给出只包含毫秒时间的不完整流ID:在这种情况下,Redis会自动将ID的顺序编号部分设置为0.

xadd s1 1000000000000  k1 v1
# 返回1000000000000-0
xadd s1 2000000000000  k1 v1
# 返回2000000000000-0
流元素ID的限制

同一流中的每个元素ID都用于指定特定的一个元素,所以这些ID必须唯一。尝试添加一个也存在ID的元素会返回下面这个错误。

ERR The ID specified in XADD is equal or smaller than the target stream top item
除了不允许使用相同ID之外,Redis还要求新元素的ID必须比流中所有已有元素的ID都要大。Redis会记住每个流已有元素的最大ID,并在用户尝试向流里面添加新的元素的时候,使用新元素的ID与流目前最大的ID进行对比,对比规则如下:

  • 如果新ID的毫秒时间部分比最大的毫秒时间部分要大,那么允许添加新元素。
  • 如果新ID的毫秒时间部分与最大ID的毫秒时间部分相同,那么对比两个ID的顺序部分,如果新ID的顺序编号部分比最大ID的顺序编号部分要大,那么允许添加新元素。
  • 不满足上述两种规则,添加操作会被拒绝,返回错误。
自动生成ID

由于流元素ID的严格特性,Redis为了便于实现添加操作,XADD命令的id参数设定了一个特殊值"“,当用户将符号”"用作id参数的值时,Redis自动会新添加的元素生成一个可用的新ID。

自动生成的新ID会将Redis所在的宿主机器当前的毫秒格式的UNIX时间戳用作ID的毫秒时间,并根据当前已有ID的最大顺序编号来设置新ID的顺序编号部分。

由于自动生成ID时间戳与宿主机当前时间戳有关, 防止宿主机出现时钟错误导致问题出现,redis的宿主机器当前时间戳比流中已使用最大ID的毫秒时间小,那么redis将使用该ID的毫秒时间来作为新的ID的毫秒时间。

限制流的长度

XADD命令提供MAXLEN选项,让用户可以在添加新元素的同时删除旧的元素,限制流的长度

XADD STREAM [MAXLEN len] id field valeu [field value]

按照先进先出规则移除超出长度限制的元素。

其它信息

时间复杂度为O(log(N)),N为流目前包含的元素数量。

XTRIM对流进行修建

通过XTRIM命令直接将流修剪至指定长度

XTRIM stream MAXLEN len

同样使用先进先出规则进行元素丢弃。

其它信息

复杂度为O(log(N)+ M),N为执行修剪操作前流包含的元素数量,M为被移除元素的数量。

XDEL:移除指定元素

XDEL命令接受一个流以及任意多个元素ID作为输入,并从流中移除ID对应的元素,返回被移除元素的数量作为结果。

XDEL stream [id id ... id]
# 例如
xdel s1 2000000000000-0
# 返回1
其它信息

复杂度:O(log(N)* M),N为流所包含的元素数量,M被移除元素的数量。

XLEN获取流包含的元素数量

通过对流指定XLEN命令,获取流目前包含的元素数量。

XLEN stream

如果给定的流没有任务元素,或者流不存在,返回0作为结果。

其它信息

复杂度:O(1)

XRANGE、XREVRANGE:访问流中元素

**流本质是一个有序序列。**使用有序序列获取序列中的各项元素是一种非常常见的操作。XRANGE命令可以遍历或者迭代方式访问流中单个或者任意多个元素。可以使用可选参数限制返回数量。

XRANGE stream start-id end-id [count n]
获取指定ID的单个元素

XRANGE命令将起始ID和结束ID设置为同一个流元素ID,这样XRANGE命令就会从流中获取并返回ID指定的流元素。

xrange s1 1000000000000-0  1000000000000-0
#返回值
1) 1) "1000000000000-0" #流元素ID
   2) 1) "k1"           # 流元素包含的键   
      2) "v1"           # 流元素包含的值

如果指定ID不存在,会返回一个空列表作为结果。

获取指定ID范围内的多个元素

只需要将较小的元素ID设置为命令的起始ID,并将较大的元素ID设置为命令的结束ID,那么XRANGE命令就会获取从起始ID到结束ID范围内的所有元素。

XRANGE s1 1000000000000 4000000000000

Xrange命令的起始ID和结束ID除了可以是流元素的ID之外,还可以是特殊值-和+,前者用于表示流的最小ID,后者是流的最大ID。通过-和+可以获取流中所有元素。

对流中元素进行迭代

使用- + 和可选参数进行流的迭代。

xrange stream - + count 1
#加入返回id为 1000000000000-1 
xrange stream 1000000000000-2 + count 1
# 以此类推
逆序访问流中元素

XREVRANGE命令是XRANGE命令的逆序版本,除了按照ID从大到小访问,其它都一样。

其它信息

复杂度O(log(N)+M),N为流中包含的元素数量,M为命令范围的元素数量。

XREAD:以阻塞或非阻塞方式获取流元素

XREAD [BLOCK ms] [COUTN N] STREAMS stream1 stream2 ... id1 id2 ...

XREAD只能从一个方向对流进行迭代,但是他能提供更简单的迭代API,支持同时对多个流进行迭代,并且能够以阻塞和非阻塞两种方式执行。

获取大于指定ID的元素

可以通过COUNT选项限制每个流最多可以返回的元素

XREAD COUTN 3 Streams s1 s2 1000000000000-0 2000000000-0
阻塞

通过使用BLOCK选项并给定一个毫秒精度的超时时间作为参数,用于可以以阻塞的方式执行XREAD命令

XREAD [BLOCK ms] [COUNT n] STREAMS streams1 streams2 ... id1 id2 ...

BLOCK选项值可以是任何大于等于0的数值,给定0表示阻塞直至出现可返回的元素位置
如果在用户给定的流中,有一个或多个流拥有符合条件、可供读取的元素,那么XREAD命令将直接返回这些元素而不会进入阻塞状态。都不符合条件才会进入阻塞状态。

只获取新出现的元素

Redis为XREAD命令提供了特殊ID参数$。XREAD命令只会获取给定流在命令流执行之后新出现的元素

XREAD BLOCK 1000000 STREAMS bs1 $
其它信息

时间复杂度:获取每个流的元素 O(log (N) + M),其中N为流包含的元素数量,M为被获取的元素数量。因此对于用户给定的I个流,获取流元素的总复杂度为O((log (N) + M)*I)。

<think>嗯,用户想了解Redis 6.0支持的数据结构及其特性。这个问题很专业,需要系统整理Redis核心数据结构在6.0版本中的实现和优化。 首先回忆Redis 6.0的重要更新,虽然数据结构本身没有新增类型,但内存管理和线程模型有重大改进。用户引用的资料[1][4]提到6.0版本提升了性能和扩展性,特别是客户端缓存特性[^4],这对数据结构的使用效率有间接影响。 从基础数据结构开始梳理: 字符串(String)作为最基础的类型,在6.0中embstr编码优化了短字符串存储,引用[2]提到的list结构体其实也适用于所有数据结构——Redis都采用类似的设计模式:用专属结构体管理元数据(如len字段),再指向具体存储节点。哈希表(Hash)的ziplist转hashtable的阈值值得说明,这个直接影响内存效率。 列表(List)要重点讲quicklist结构,这是3.2版本引入的,但6.0的线程IO优化使其并发性能提升。用户引用[2]的list结构体代码很关键,其中dup和free函数指针说明Redis支持深拷贝[^2]。集合(Set)的整数集合升级机制需要举例说明,比如$SADD$命令触发intset转hashtable的过程。 有序集合(ZSet)的ziplist+skiplist双结构是难点,要解释跳表如何实现$O(logN)$查询。引用[4]的客户端缓存特性[^4]对频繁访问的有序集合特别有利,因为可以避免重复查询。位图(Bitmap)和HyperLogLog虽然基于字符串,但它们的位操作指令如$SETBIT$需要单独说明。 地理空间索引(Geospatial)本质是有序集合,但GEOADD命令的坐标编码机制值得展开。流(Stream)作为5.0新增结构,在6.0中稳定性增强,要强调消息ID的时序性和消费者组特性。 最后补充6.0的线程安全改进:虽然数据结构操作本身还是单线程,但网络IO多线程化提升了大value操作的吞吐量。引用[3]的bind配置[^3]看似无关,实则影响数据结构服务稳定性——错误的网络配置可能导致数据丢失。 用户可能混淆了数据结构版本差异,需要明确6.0是优化而非新增结构。相关问题的设计要覆盖使用场景(如为什么选Hash而不是String存储对象)和性能对比(如ZSet的跳表查询复杂度证明)。</think>Redis 6.0 延续了其丰富的数据结构支持,以下是核心数据结构及其特性的详细解析: ### 一、核心数据结构 1. **字符串(String)** - 存储文本、二进制数据(如图片)或数值 - 最大容量 512MB - 特殊操作: ```bash INCR counter # 原子计数器 APPEND log "msg" # 追加操作 BITCOUNT user:1 # 位运算 ``` 2. **哈希(Hash)** - 存储字段-值映射表,适合对象存储 - 底层实现: - `ziplist`(元素少时,内存紧凑) - `hashtable`(元素多时,$O(1)$ 复杂度) - 示例: ```bash HSET user:1000 name "Alice" age 30 HGETALL user:1000 ``` 3. **列表(List)** - 双向链表结构,支持左右插入/弹出 - 关键特性: - 可实现队列(LPUSH/RPOP)或栈(LPUSH/LPOP) - 阻塞操作:`BLPOP` 实现消息队列 - 底层结构:`quicklist`(ziplist 链表,平衡内存和速度) 4. **集合(Set)** - 无序唯一元素集合 - 支持交并差集运算($SINTER/SUNION/SDIFF$) - 底层实现: - `intset`(纯整数时) - `hashtable`(其他类型) 5. **有序集合(Sorted Set)** - 元素关联分值,按分值排序 - 底层结构: - `ziplist`(小规模) - **跳表(skiplist)+ 字典**(大规模,$O(logN)$ 复杂度) - 范围操作: ```bash ZRANGEBYSCORE players 1000 2000 # 按分值检索 ``` ### 二、高级数据结构 6. **位图(Bitmap)** - 基于字符串的位操作 - 适用场景:实时用户签到统计 ```bash SETBIT user:2024:sign 15 1 # 记录第15天签到 BITCOUNT user:2024:sign # 统计总签到 ``` 7. **HyperLogLog** - 基数估算(误差约 0.81%) - 内存仅需 12KB ```bash PFADD ip_log 192.168.1.1 PFCOUNT ip_log # 估算独立IP数 ``` 8. **地理空间索引(Geospatial)** - 存储经纬度坐标 - 支持距离计算(GEODIST)和半径查询(GEORADIUS) ```bash GEOADD cities 116.40 39.90 Beijing GEOSEARCH cities FROMLONLAT 116.4 39.9 BYRADIUS 100 km ``` 9. **流(Stream)** - 消息队列实现(Redis 5.0+) - 特性: - 消息持久化 - 消费者组(Consumer Groups) - 消息回溯 ```bash XADD orders * product "Book" price 29.9 XREAD GROUP group1 consumer1 COUNT 1 STREAMS orders > ``` ### 三、Redis 6.0 的增强特性 1. **多线程网络 I/O** - 处理网络请求使用多线程(执行命令仍单线程) - 提升高并发场景吞吐量 2. **客户端缓存(Client-side Caching)** - 服务端跟踪客户端缓存(`RESP3` 协议支持) - 主动通知缓存失效(避免脏读)[^4] 3. **ACL 权限控制** - 细粒度命令权限管理 ```bash ACL SETUSER alice ON >pass123 +@read ~cache:* ``` 4. **TLS 加密传输** - 增强客户端-服务端通信安全[^3] ### 四、数据结构选择建议 | 场景 | 推荐结构 | 原因 | |------|----------|------| | 对象属性存储 | Hash | 字段独立存取 | | 排行榜 | Sorted Set | 天然排序能力 | | 消息队列 | Stream | 消息持久化和消费组 | | UV统计 | HyperLogLog | 低内存消耗 | > Redis 6.0 通过优化内存分配(jemalloc 改进)和网络层多线程,显著提升了大型数据结构(如数十万元素的 Sorted Set)的操作效率[^1][^4]。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

无限码力

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值