Redis入门 – 数据类型:Stream详解

redis5.0 中还增加了一个数据类型stream,它借鉴了kafka的设计,是一个新的强大的支持多播的可持久化的消息队列。@pdai

  • redis入门 – 数据类型:stream详解
    • 为什么会设计stream
    • stream详解
      • stream的结构
      • 增删改查
      • 独立消费
      • 消费组消费
      • 信息监控
    • 更深入理解
      • stream用在什么样场景
      • 消息id的设计是否考虑了时间回拨的问题?
      • 消费者崩溃带来的会不会消息丢失问题?
      • 消费者彻底宕机后如何转移给其它消费者处理?
      • 坏消息问题,dead letter,死信问题
    • 参考文章

为什么会设计stream

redis5.0 中还增加了一个数据结构stream,从字面上看是流类型,但其实从功能上看,应该是redis对消息队列(mq,message queue)的完善实现。

用过redis做消息队列的都了解,基于reids的消息队列实现有很多种,例如:

  • pub/sub,订阅/发布模式
    • 但是发布订阅模式是无法持久化的,如果出现网络断开、redis 宕机等,消息就会被丢弃;
  • 基于list lpush+brpop 或者 基于sorted-set的实现
    • 支持了持久化,但是不支持多播,分组消费等

为什么上面的结构无法满足广泛的mq场景? 这里便引出一个核心的问题:如果我们期望设计一种数据结构来实现消息队列,最重要的就是要理解设计一个消息队列需要考虑什么?初步的我们很容易想到

  • 消息的生产
  • 消息的消费
    • 单播和多播(多对多)
    • 阻塞和非阻塞读取
  • 消息有序性
  • 消息的持久化

其它还要考虑啥嗯?借助美团技术团队的一篇文章, 中的图

我们不妨看看redis考虑了哪些设计

  • 消息id的序列化生成
  • 消息遍历
  • 消息的阻塞和非阻塞读取
  • 消息的分组消费
  • 未完成消息的处理
  • 消息队列监控

这也是我们需要理解stream的点,但是结合上面的图,我们也应该理解redis stream也是一种超轻量mq并没有完全实现消息队列所有设计要点,这决定着它适用的场景。

stream详解

经过梳理总结,我认为从以下几个大的方面去理解stream是比较合适的,总结如下:@pdai

  • stream的结构设计
  • 生产和消费
    • 基本的增删查改
    • 单一消费者的消费
    • 消费组的消费
  • 监控状态

stream的结构

每个 stream 都有唯一的名称,它就是 redis 的 key,在我们首次使用 xadd 指令追加消息时自动创建。

上图解析:

  • consumer group :消费组,使用 xgroup create 命令创建,一个消费组有多个消费者(consumer), 这些消费者之间是竞争关系。
  • last_delivered_id :游标,每个消费组会有个游标 last_delivered_id,任意一个消费者读取了消息都会使游标 last_delivered_id 往前移动。
  • pending_ids :消费者(consumer)的状态变量,作用是维护消费者的未确认的 id。 pending_ids 记录了当前已经被客户端读取的消息,但是还没有 ack (acknowledge character:确认字符)。如果客户端没有ack,这个变量里面的消息id会越来越多,一旦某个消息被ack,它就开始减少。这个pending_ids变量在redis官方被称之为pel,也就是pending entries list,这是一个很核心的数据结构,它用来确保客户端至少消费了消息一次,而不会在网络传输的中途丢失了没处理。

此外我们还需要理解两点:

  • 消息id: 消息id的形式是timestampinmillis-sequence,例如1527846880572-5,它表示当前的消息在毫米时间戳1527846880572时产生,并且是该毫秒内产生的第5条消息。消息id可以由服务器自动生成,也可以由客户端自己指定,但是形式必须是整数-整数,而且必须是后面加入的消息的id要大于前面的消息id。
  • 消息内容: 消息内容就是键值对,形如hash结构的键值对,这没什么特别之处。

增删改查

消息队列相关命令:

  • xadd – 添加消息到末尾
  • xtrim – 对流进行修剪,限制长度
  • xdel – 删除消息
  • xlen – 获取流包含的元素数量,即消息长度
  • xrange – 获取消息列表,会自动过滤已经删除的消息
  • xrevrange – 反向获取消息列表,id 从大到小
  • xread – 以阻塞或非阻塞方式获取消息列表
# *号表示服务器自动生成id,后面顺序跟着一堆key/value
127.0.0.1:6379> xadd codehole * name laoqian age 30  #  名字叫laoqian,年龄30岁
1527849609889-0  # 生成的消息id
127.0.0.1:6379> xadd codehole * name xiaoyu age 29
1527849629172-0
127.0.0.1:6379> xadd codehole * name xiaoqian age 1
1527849637634-0
127.0.0.1:6379> xlen codehole
(integer) 3
127.0.0.1:6379> xrange codehole - +  # -表示最小值, +表示最大值
127.0.0.1:6379> xrange codehole - +
1) 1) 1527849609889-0
   1) 1) "name"
      1) "laoqian"
      2) "age"
      3) "30"
2) 1) 1527849629172-0
   1) 1) "name"
      1) "xiaoyu"
      2) "age"
      3) "29"
3) 1) 1527849637634-0
   1) 1) "name"
      1) "xiaoqian"
      2) "age"
      3) "1"
127.0.0.1:6379> xrange codehole 1527849629172-0 +  # 指定最小消息id的列表
1) 1) 1527849629172-0
   2) 1) "name"
      2) "xiaoyu"
      3) "age"
      4) "29"
2) 1) 1527849637634-0
   2) 1) "name"
      2) "xiaoqian"
      3) "age"
      4) "1"
127.0.0.1:6379> xrange codehole - 1527849629172-0  # 指定最大消息id的列表
1) 1) 1527849609889-0
   2) 1) "name"
      2) "laoqian"
      3) "age"
      4) "30"
2) 1) 1527849629172-0
   2) 1) "name"
      2) "xiaoyu"
      3) "age"
      4) "29"
127.0.0.1:6379> xdel codehole 1527849609889-0
(integer) 1
127.0.0.1:6379> xlen codehole  # 长度不受影响
(integer) 3
127.0.0.1:6379> xrange codehole - +  # 被删除的消息没了
1) 1) 1527849629172-0
   2) 1) "name"
      2) "xiaoyu"
      3) "age"
      4) "29"
2) 1) 1527849637634-0
   2) 1) "name"
      2) "xiaoqian"
      3) "age"
      4) "1"
127.0.0.1:6379> del codehole  # 删除整个stream
(integer) 1

独立消费

我们可以在不定义消费组的情况下进行stream消息的独立消费,当stream没有新消息时,甚至可以阻塞等待。redis设计了一个单独的消费指令xread,可以将stream当成普通的消息队列(list)来使用。使用xread时,我们可以完全忽略消费组(consumer group)的存在,就好比stream就是一个普通的列表(list)。

# 从stream头部读取两条消息
127.0.0.1:6379> xread count 2 streams codehole 0-0
1) 1) "codehole"
   2) 1) 1) 1527851486781-0
         2) 1) "name"
            2) "laoqian"
            3) "age"
            4) "30"
      2) 1) 1527851493405-0
         2) 1) "name"
            2) "yurui"
            3) "age"
            4) "29"
# 从stream尾部读取一条消息,毫无疑问,这里不会返回任何消息
127.0.0.1:6379> xread count 1 streams codehole $
(nil)
# 从尾部阻塞等待新消息到来,下面的指令会堵住,直到新消息到来
127.0.0.1:6379> xread block 0 count 1 streams codehole $
# 我们从新打开一个窗口,在这个窗口往stream里塞消息
127.0.0.1:6379> xadd codehole * name youming age 60
1527852774092-0
# 再切换到前面的窗口,我们可以看到阻塞解除了,返回了新的消息内容
# 而且还显示了一个等待时间,这里我们等待了93s
127.0.0.1:6379> xread block 0 count 1 streams codehole $
1) 1) "codehole"
   2) 1) 1) 1527852774092-0
         2) 1) "name"
            2) "youming"
            3) "age"
            4) "60"
(93.11s)

客户端如果想要使用xread进行顺序消费,一定要记住当前消费到哪里了,也就是返回的消息id。下次继续调用xread时,将上次返回的最后一个消息id作为参数传递进去,就可以继续消费后续的消息。

block 0表示永远阻塞,直到消息到来,block 1000表示阻塞1s,如果1s内没有任何消息到来,就返回nil

127.0.0.1:6379> xread block 1000 count 1 streams codehole $
(nil)
(1.07s)

消费组消费

  • 消费组消费图
  • 相关命令:

    • xgroup create – 创建消费者组
    • xreadgroup group – 读取消费者组中的消息
    • xack – 将消息标记为”已处理”
    • xgroup setid – 为消费者组设置新的最后递送消息id
    • xgroup delconsumer – 删除消费者
    • xgroup destroy – 删除消费者组
    • xpending – 显示待处理消息的相关信息
    • xclaim – 转移消息的归属权
    • xinfo – 查看流和消费者组的相关信息;
    • xinfo groups – 打印消费者组的信息;
    • xinfo stream – 打印流信息
  • 创建消费组

stream通过xgroup create指令创建消费组(consumer group),需要传递起始消息id参数用来初始化last_delivered_id变量。

127.0.0.1:6379> xgroup create codehole cg1 0-0  #  表示从头开始消费
ok
# $表示从尾部开始消费,只接受新消息,当前stream消息会全部忽略
127.0.0.1:6379> xgroup create codehole cg2 $
ok
127.0.0.1:6379> xinfo stream codehole  # 获取stream信息
 1) length
 2) (integer) 3  # 共3个消息
 3) radix-tree-keys
 4) (integer) 1
 5) radix-tree-nodes
 6) (integer) 2
 7) groups
 8) (integer) 2  # 两个消费组
 9) first-entry  # 第一个消息
10) 1) 1527851486781-0
    2) 1) "name"
       2) "laoqian"
       3) "age"
       4) "30"
11) last-entry  # 最后一个消息
12) 1) 1527851498956-0
    2) 1) "name"
       2) "xiaoqian"
       3) "age"
       4) "1"
127.0.0.1:6379> xinfo groups codehole  # 获取stream的消费组信息
1) 1) name
   2) "cg1"
   3) consumers
   4) (integer) 0  # 该消费组还没有消费者
   5) pending
   6) (integer) 0  # 该消费组没有正在处理的消息
2) 1) name
   2) "cg2"
   3) consumers  # 该消费组还没有消费者
   4) (integer) 0
   5) pending
   6) (integer) 0  # 该消费组没有正在处理的消息
  • 消费组消费

stream提供了xreadgroup指令可以进行消费组的组内消费,需要提供消费组名称、消费者名称和起始消息id。它同xread一样,也可以阻塞等待新消息。读到新消息后,对应的消息id就会进入消费者的pel(正在处理的消息)结构里,客户端处理完毕后使用xack指令通知服务器,本条消息已经处理完毕,该消息id就会从pel中移除。

# >号表示从当前消费组的last_delivered_id后面开始读
# 每当消费者读取一条消息,last_delivered_id变量就会前进
127.0.0.1:6379> xreadgroup group cg1 c1 count 1 streams codehole >
1) 1) "codehole"
   2) 1) 1) 1527851486781-0
         2) 1) "name"
            2) "laoqian"
            3) "age"
            4) "30"
127.0.0.1:6379> xreadgroup group cg1 c1 count 1 streams codehole >
1) 1) "codehole"
   2) 1) 1) 1527851493405-0
         2) 1) "name"
            2) "yurui"
            3) "age"
            4) "29"
127.0.0.1:6379> xreadgroup group cg1 c1 count 2 streams codehole >
1) 1) "codehole"
   2) 1) 1) 1527851498956-0
         2) 1) "name"
            2) "xiaoqian"
            3) "age"
            4) "1"
      2) 1) 1527852774092-0
         2) 1) "name"
            2) "youming"
            3) "age"
            4) "60"
# 再继续读取,就没有新消息了
127.0.0.1:6379> xreadgroup group cg1 c1 count 1 streams codehole >
(nil)
# 那就阻塞等待吧
127.0.0.1:6379> xreadgroup group cg1 c1 block 0 count 1 streams codehole >
# 开启另一个窗口,往里塞消息
127.0.0.1:6379> xadd codehole * name lanying age 61
1527854062442-0
# 回到前一个窗口,发现阻塞解除,收到新消息了
127.0.0.1:6379> xreadgroup group cg1 c1 block 0 count 1 streams codehole >
1) 1) "codehole"
   2) 1) 1) 1527854062442-0
         2) 1) "name"
            2) "lanying"
            3) "age"
            4) "61"
(36.54s)
127.0.0.1:6379> xinfo groups codehole  # 观察消费组信息
1) 1) name
   2) "cg1"
   3) consumers
   4) (integer) 1  # 一个消费者
   5) pending
   6) (integer) 5  # 共5条正在处理的信息还有没有ack
2) 1) name
   2) "cg2"
   3) consumers
   4) (integer) 0  # 消费组cg2没有任何变化,因为前面我们一直在操纵cg1
   5) pending
   6) (integer) 0
# 如果同一个消费组有多个消费者,我们可以通过xinfo consumers指令观察每个消费者的状态
127.0.0.1:6379> xinfo consumers codehole cg1  # 目前还有1个消费者
1) 1) name
   2) "c1"
   3) pending
   4) (integer) 5  # 共5条待处理消息
   5) idle
   6) (integer) 418715  # 空闲了多长时间ms没有读取消息了
# 接下来我们ack一条消息
127.0.0.1:6379> xack codehole cg1 1527851486781-0
(integer) 1
127.0.0.1:6379> xinfo consumers codehole cg1
1) 1) name
   2) "c1"
   3) pending
   4) (integer) 4  # 变成了5条
   5) idle
   6) (integer) 668504
# 下面ack所有消息
127.0.0.1:6379> xack codehole cg1 1527851493405-0 1527851498956-0 1527852774092-0 1527854062442-0
(integer) 4
127.0.0.1:6379> xinfo consumers codehole cg1
1) 1) name
   2) "c1"
   3) pending
   4) (integer) 0  # pel空了
   5) idle
   6) (integer) 745505

信息监控

stream提供了xinfo来实现对服务器信息的监控,可以查询:

  • 查看队列信息
127.0.0.1:6379> xinfo stream mq
 1) "length"
 2) (integer) 7
 3) "radix-tree-keys"
 4) (integer) 1
 5) "radix-tree-nodes"
 6) (integer) 2
 7) "groups"
 8) (integer) 1
 9) "last-generated-id"
10) "1553585533795-9"
11) "first-entry"
12) 1) "1553585533795-3"
    2) 1) "msg"
       2) "4"
13) "last-entry"
14) 1) "1553585533795-9"
    2) 1) "msg"
       2) "10"
  • 消费组信息
127.0.0.1:6379> xinfo groups mq
1) 1) "name"
   2) "mqgroup"
   3) "consumers"
   4) (integer) 3
   5) "pending"
   6) (integer) 3
   7) "last-delivered-id"
   8) "1553585533795-4"
  • 消费者组成员信息
127.0.0.1:6379> xinfo consumers mq mqgroup
1) 1) "name"
   2) "consumera"
   3) "pending"
   4) (integer) 1
   5) "idle"
   6) (integer) 18949894
2) 1) "name"
   2) "consumerb"
   3) "pending"
   4) (integer) 1
   5) "idle"
   6) (integer) 3092719
3) 1) "name"
   2) "consumerc"
   3) "pending"
   4) (integer) 1
   5) "idle"
   6) (integer) 23683256

至此,消息队列的操作说明大体结束!

更深入理解

我们结合mq中常见问题,看redis是如何解决的,来进一步理解redis。

stream用在什么样场景

可用作时通信等,大数据分析,异地数据备份等

客户端可以平滑扩展,提高处理能力

消息id的设计是否考虑了时间回拨的问题?

在 分布式算法 – id算法设计中, 一个常见的问题就是时间回拨问题,那么redis的消息id设计中是否考虑到这个问题呢?

xadd生成的1553439850328-0,就是redis生成的消息id,由两部分组成:时间戳-序号。时间戳是毫秒级单位,是生成消息的redis服务器时间,它是个64位整型(int64)。序号是在这个毫秒时间点内的消息序号,它也是个64位整型。

可以通过multi批处理,来验证序号的递增:

127.0.0.1:6379> multi
ok
127.0.0.1:6379> xadd membermessage * msg one
queued
127.0.0.1:6379> xadd membermessage * msg two
queued
127.0.0.1:6379> xadd membermessage * msg three
queued
127.0.0.1:6379> xadd membermessage * msg four
queued
127.0.0.1:6379> xadd membermessage * msg five
queued
127.0.0.1:6379> exec
1) "1553441006884-0"
2) "1553441006884-1"
3) "1553441006884-2"
4) "1553441006884-3"
5) "1553441006884-4"

由于一个redis命令的执行很快,所以可以看到在同一时间戳内,是通过序号递增来表示消息的。

为了保证消息是有序的,因此redis生成的id是单调递增有序的。由于id中包含时间戳部分,为了避免服务器时间错误而带来的问题(例如服务器时间延后了),redis的每个stream类型数据都维护一个latest_generated_id属性,用于记录最后一个消息的id。若发现当前时间戳退后(小于latest_generated_id所记录的),则采用时间戳不变而序号递增的方案来作为新消息id(这也是序号为什么使用int64的原因,保证有足够多的的序号),从而保证id的单调递增性质。

强烈建议使用redis的方案生成消息id,因为这种时间戳+序号的单调递增的id方案,几乎可以满足你全部的需求。但同时,记住id是支持自定义的,别忘了!

消费者崩溃带来的会不会消息丢失问题?

为了解决组内消息读取但处理期间消费者崩溃带来的消息丢失问题,stream 设计了 pending 列表,用于记录读取但并未处理完毕的消息。命令xpendiing 用来获消费组或消费内消费者的未处理完毕的消息。演示如下:

127.0.0.1:6379> xpending mq mqgroup # mpgroup的pending情况
1) (integer) 5 # 5个已读取但未处理的消息
2) "1553585533795-0" # 起始id
3) "1553585533795-4" # 结束id
4) 1) 1) "consumera" # 消费者a有3个
      2) "3"
   2) 1) "consumerb" # 消费者b有1个
      2) "1"
   3) 1) "consumerc" # 消费者c有1个
      2) "1"

127.0.0.1:6379> xpending mq mqgroup - + 10 # 使用 start end count 选项可以获取详细信息
1) 1) "1553585533795-0" # 消息id
   2) "consumera" # 消费者
   3) (integer) 1654355 # 从读取到现在经历了1654355ms,idle
   4) (integer) 5 # 消息被读取了5次,delivery counter
2) 1) "1553585533795-1"
   2) "consumera"
   3) (integer) 1654355
   4) (integer) 4
# 共5个,余下3个省略 ...

127.0.0.1:6379> xpending mq mqgroup - + 10 consumera # 在加上消费者参数,获取具体某个消费者的pending列表
1) 1) "1553585533795-0"
   2) "consumera"
   3) (integer) 1641083
   4) (integer) 5
# 共3个,余下2个省略 ...

每个pending的消息有4个属性:

  • 消息id
  • 所属消费者
  • idle,已读取时长
  • delivery counter,消息被读取次数

上面的结果我们可以看到,我们之前读取的消息,都被记录在pending列表中,说明全部读到的消息都没有处理,仅仅是读取了。那如何表示消费者处理完毕了消息呢?使用命令 xack 完成告知消息处理完成,演示如下:

127.0.0.1:6379> xack mq mqgroup 1553585533795-0 # 通知消息处理结束,用消息id标识
(integer) 1

127.0.0.1:6379> xpending mq mqgroup # 再次查看pending列表
1) (integer) 4 # 已读取但未处理的消息已经变为4个
2) "1553585533795-1"
3) "1553585533795-4"
4) 1) 1) "consumera" # 消费者a,还有2个消息处理
      2) "2"
   2) 1) "consumerb"
      2) "1"
   3) 1) "consumerc"
      2) "1"
127.0.0.1:6379>

有了这样一个pending机制,就意味着在某个消费者读取消息但未处理后,消息是不会丢失的。等待消费者再次上线后,可以读取该pending列表,就可以继续处理该消息了,保证消息的有序和不丢失。

消费者彻底宕机后如何转移给其它消费者处理?

还有一个问题,就是若某个消费者宕机之后,没有办法再上线了,那么就需要将该消费者pending的消息,转义给其他的消费者处理,就是消息转移。

消息转移的操作时将某个消息转移到自己的pending列表中。使用语法xclaim来实现,需要设置组、转移的目标消费者和消息id,同时需要提供idle(已被读取时长),只有超过这个时长,才能被转移。演示如下:

# 当前属于消费者a的消息1553585533795-1,已经15907,787ms未处理了
127.0.0.1:6379> xpending mq mqgroup - + 10
1) 1) "1553585533795-1"
   2) "consumera"
   3) (integer) 15907787
   4) (integer) 4

# 转移超过3600s的消息1553585533795-1到消费者b的pending列表
127.0.0.1:6379> xclaim mq mqgroup consumerb 3600000 1553585533795-1
1) 1) "1553585533795-1"
   2) 1) "msg"
      2) "2"

# 消息1553585533795-1已经转移到消费者b的pending中。
127.0.0.1:6379> xpending mq mqgroup - + 10
1) 1) "1553585533795-1"
   2) "consumerb"
   3) (integer) 84404 # 注意idle,被重置了
   4) (integer) 5 # 注意,读取次数也累加了1次

以上代码,完成了一次消息转移。转移除了要指定id外,还需要指定idle,保证是长时间未处理的才被转移。被转移的消息的idle会被重置,用以保证不会被重复转移,以为可能会出现将过期的消息同时转移给多个消费者的并发操作,设置了idle,则可以避免后面的转移不会成功,因为idle不满足条件。例如下面的连续两条转移,第二条不会成功。

127.0.0.1:6379> xclaim mq mqgroup consumerb 3600000 1553585533795-1
127.0.0.1:6379> xclaim mq mqgroup consumerc 3600000 1553585533795-1

这就是消息转移。至此我们使用了一个pending消息的id,所属消费者和idle的属性,还有一个属性就是消息被读取次数,delivery counter,该属性的作用由于统计消息被读取的次数,包括被转移也算。这个属性主要用在判定是否为错误数据上。

坏消息问题,dead letter,死信问题

正如上面所说,如果某个消息,不能被消费者处理,也就是不能被xack,这是要长时间处于pending列表中,即使被反复的转移给各个消费者也是如此。此时该消息的delivery counter就会累加(上一节的例子可以看到),当累加到某个我们预设的临界值时,我们就认为是坏消息(也叫死信,deadletter,无法投递的消息),由于有了判定条件,我们将坏消息处理掉即可,删除即可。删除一个消息,使用xdel语法,演示如下:

# 删除队列中的消息
127.0.0.1:6379> xdel mq 1553585533795-1
(integer) 1
# 查看队列中再无此消息
127.0.0.1:6379> xrange mq - +
1) 1) "1553585533795-0"
   2) 1) "msg"
      2) "1"
2) 1) "1553585533795-2"
   2) 1) "msg"
      2) "3"

注意本例中,并没有删除pending中的消息因此你查看pending,消息还会在。可以执行xack标识其处理完毕!

参考文章

本文主要梳理总结自:

知识体系

知识体系

相关文章

首先,我们通过学习redis的概念基础,了解它适用的场景。

  • redis入门 – redis概念和基础
    • redis是一种支持key-value等多种数据结构的存储系统。可用于缓存,事件发布或订阅,高速队列等场景。支持网络,提供字符串,哈希,列表,队列,集合结构直接存取,基于内存,可持久化。

其次,这些适用场景都是基于redis支持的数据类型的,所以我们需要学习它支持的数据类型;同时在redis优化中还需要对底层数据结构了解,所以也需要了解一些底层数据结构的设计和实现。

  • redis入门 – 数据类型:5种基础数据类型详解
    • redis所有的key(键)都是字符串。我们在谈基础数据结构时,讨论的是存储值的数据类型,主要包括常见的5种数据类型,分别是:string、list、set、zset、hash
  • redis入门 – 数据类型:3种特殊类型详解
    • redis除了上文中5种基础数据类型,还有三种特殊的数据类型,分别是 hyperloglogs(基数统计), bitmaps (位图) 和 geospatial (地理位置)
  • redis入门 – 数据类型:stream详解
    • redis5.0 中还增加了一个数据结构stream,它借鉴了kafka的设计,是一个新的强大的支持多播的可持久化的消息队列。
  • redis进阶 – 底层数据结构:对象机制详解
    • 我们在前文已经阐述了redis 5种基础数据类型详解,分别是字符串(string)、列表(list)、哈希(hash)、集合(set)、有序集合(zset),以及5.0版本中redis stream结构详解;那么这些基础类型的底层是如何实现的呢?redis的每种对象其实都由对象结构(redisobject)对应编码的数据结构组合而成, 本文主要介绍对象结构(redisobject) 部分。。
  • redis进阶 – 底层数据结构:底层数据结构详解
    • 前文是第一部分底层设计:对象机制详解, 本文主要介绍底层数据结构 部分。
  • redis进阶 – 底层数据结构:redis对象与编码(底层结构)对应关系详解
    • 在学习完之后,我们终于可以结合前文内容阐述redis对象及编码之间的关系了。

再者,需要学习redis支持的核心功能,包括持久化,消息,事务,高可用;高可用方面包括,主从,哨兵等;高可拓展方面,比如 分片机制等。

  • redis进阶 – 持久化:rdb和aof机制详解
    • 为了防止数据丢失以及服务重启时能够恢复数据,redis支持数据的持久化,主要分为两种方式,分别是rdb和aof; 当然实际场景下还会使用这两种的混合模式。
  • redis进阶 – 消息传递:发布订阅模式详解
    • redis 发布订阅(pub/sub)是一种消息通信模式:发送者(pub)发送消息,订阅者(sub)接收消息。
  • redis进阶 – 事件:redis事件机制详解
    • redis 采用事件驱动机制来处理大量的网络io。它并没有使用 libevent 或者 libev 这样的成熟开源方案,而是自己实现一个非常简洁的事件驱动库 ae_event。
  • redis进阶 – 事务:redis事务详解
    • redis 事务的本质是一组命令的集合。事务支持一次执行多个命令,一个事务中所有命令都会被序列化。在事务执行过程,会按照顺序串行化执行队列中的命令,其他客户端提交的命令请求不会插入到事务执行命令序列中。
  • redis进阶 – 高可用:主从复制详解
    • 我们知道要避免单点故障,即保证高可用,便需要冗余(副本)方式提供集群服务。而redis 提供了主从库模式,以保证数据副本的一致,主从库之间采用的是读写分离的方式。本文主要阐述redis的主从复制。
  • redis进阶 – 高可用:哨兵机制(redis sentinel)详解
    • 在上文主从复制的基础上,如果注节点出现故障该怎么办呢? 在 redis 主从集群中,哨兵机制是实现主从库自动切换的关键机制,它有效地解决了主从复制模式下故障转移的问题。
  • redis进阶 – 高可拓展:分片技术(redis cluster)详解
    • 前面两篇文章,主从复制和哨兵机制保障了高可用,就读写分离而言虽然slave节点来扩展主从的读并发能力,但是写能力和存储能力是无法进行扩展的,就只能是master节点能够承载的上限。如果面对海量数据那么必然需要构建master(主节点分片)之间的集群,同时必然需要吸收高可用(主从复制和哨兵机制)能力,即每个master分片节点还需要有slave节点,这是分布式系统中典型的纵向扩展(集群的分片技术)的体现;所以在redis 3.0版本中对应的设计就是redis cluster。

最后,就是具体的实践以及实践中遇到的问题和解决方法了:在不同版本中有不同特性,所以还需要了解版本;以及性能优化,大厂实践等。

  • redis进阶 – 缓存问题:一致性, 穿击, 穿透, 雪崩, 污染等
    • redis最常用的一个场景就是作为缓存,本文主要探讨作为缓存,在实践中可能会有哪些问题?比如一致性, 穿击, 穿透, 雪崩, 污染等
  • redis进阶 – 版本特性: redis4.0、5.0、6.0特性整理
    • 在学习redis知识体系时,我们难免会需要查看版本实现之间的差异,本文主要整理redis较为新的版本的特性。
  • redis进阶 – 运维监控:redis的监控详解
    • redis实战中包含开发,集群 和 运维,redis用的好不好,如何让它更好,这是运维要做的;本文主要在 redis自身状态及命令可视化监控工具,以及redis监控体系等方面帮助你构建对redis运维/监控体系的认知,它是性能优化的前提。
  • redis进阶 – 性能调优:redis性能调优详解
    • redis 的性能问题,涉及到的知识点非常广,几乎涵盖了 cpu、内存、网络、甚至磁盘的方方面面;同时还需要对上文中一些基础或底层有详细的了解。针对redis的性能调优,这里整理分享一篇水滴与银弹(公众号)的文章,这篇文章可以帮助你构筑redis性能调优的知识体系。
  • redis大厂经验 – 微博:万亿级日访问量下,redis在微博的9年优化历程
    • 再分享一篇微博使用redis的经验的文章,因为redis在微博内部分布在各个应用场景,比如像现在春晚必争的“红包飞”活动,还有像粉丝数、用户数、阅读数、转评赞、评论盖楼、广告推荐、负反馈、音乐榜单等等都有用到redis;我们可以通过大厂使用redis的经验来强化对redis使用上的认知。

学习资料

  • redis官网:

  • redis官方文档:

  • redis教程:

  • redis下载:

  • redis英文文档

  • redis中文文档

  • 《redis设计与实现 3.0版本》

  • redis菜鸟教程

  • redis源码解读 3.2.8版本

本篇文章由一文多发平台artipub自动发布

(0)
上一篇 2022年3月21日
下一篇 2022年3月21日

相关推荐