Redis 列表是一个有序的字符串序列,你可以从两端进行推入和弹出操作。这种简单的结构使其天然适合两种常见需求:一种是生产者添加任务、消费者获取任务的队列;另一种是仅保留最近项目的有限动态源。列表并非能解决所有队列问题(流类型更适合处理高要求场景),但对于简单的先进先出任务和“最新 N 项”动态源而言,它们是快速且正确的工具。
这是《Redis 大师课》的第五部分,继哈希之后。
推入与弹出
列表支持左右两端的操作:
LPUSH queue:emails "job1" # 添加到头部(左侧)
RPUSH queue:emails "job2" # 添加到尾部(右侧)
LPOP queue:emails # 从头部移除并返回元素
RPOP queue:emails # 从尾部移除并返回元素
LLEN queue:emails # 获取长度
LRANGE queue:emails 0 -1 # 获取所有元素
将一端的推入操作与另一端的弹出操作结合,你就得到了一个队列。LPUSH 加上 RPOP 构成先进先出模式:最旧的项目会被取出。LPUSH 加上 LPOP 构成后进先出模式,即栈。这些操作都是原子的,因此多个生产者和消费者可以安全地访问同一个列表。
简单的工作队列
基本的工作队列由生产者执行 LPUSH 推送任务,工作者执行 RPOP 获取任务:
# 生产者
LPUSH jobs:email '{"to":"a@b.com","template":"welcome"}'
# 工作者
RPOP jobs:email # 获取下一个任务,如果队列为空则返回 nil
普通 RPOP 的问题在于,当队列为空时会立即返回 nil,因此工作者必须在循环中轮询,这会消耗中央处理器资源并增加延迟。解决方案是使用阻塞变体:
BRPOP jobs:email 5 # 等待最多 5 秒以获取任务,然后返回
BRPOP 会阻塞直到有可用项目(或超时结束),因此工作者可以高效休眠,并在任务到达的瞬间唤醒。这将列表变成了一个无需轮询的真正基于推送的工作队列。
可靠性缺口
在使用列表作为工作队列之前,需要了解一个实际的局限性。使用 RPOP 或 BRPOP 时,一旦工作者弹出一个任务,该任务就会从列表中消失。如果工作者在处理过程中崩溃,任务就会丢失,因为它既不在队列中,也未完成。普通列表提供的是“最多一次”交付保证,这对于允许丢弃的任务来说是可以接受的,但对于不能丢失的任务来说则是错误的选择。
经典的缓解措施是使用 LMOVE(或较旧的 RPOPLPUSH),它可以在一步中原子地从队列弹出并推送到“处理中”列表:
LMOVE jobs:email jobs:processing RIGHT LEFT
现在,当工作者处理任务时,该任务位于 jobs:processing 中。如果成功,工作者会将其从中移除;如果发生崩溃,它仍保留在处理列表中,可以被恢复
免责声明:本文内容来自互联网,该文观点不代表本站观点。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,请到页面底部单击反馈,一经查实,本站将立刻删除。