Redis中的RPOP、BRPOP、LPOP 和 BLPOP

核心概念:列表的方向

在深入命令之前,必须明确 Redis 列表的"方向"概念:

  • 头部 (Left/Head): 列表的起始端。
  • 尾部 (Right/Tail): 列表的结束端。

想象一个列表 ["A", "B", "C"]

  • 头部"A"
  • 尾部"C"

1. RPOP: 从尾部弹出 (非阻塞)

命令RPOP key

功能 :移除并返回列表 key最后一个元素(即尾部元素)。

阻塞。这是一个立即返回的命令。

返回值

  • 如果列表存在且非空:返回被弹出的元素。
  • 如果列表不存在或为空:返回 nil

使用场景 :当你需要一个简单的栈 (Stack) 数据结构时(后进先出,LIFO)。

示例

bash 复制代码
# 创建一个列表
127.0.0.1:6379> RPUSH mylist "A" "B" "C"
(integer) 3

# 查看列表内容
127.0.0.1:6379> LRANGE mylist 0 -1
1) "A"
2) "B"
3) "C"

# 执行 RPOP,弹出尾部元素 "C"
127.0.0.1:6379> RPOP mylist
"C"

# 再次查看,"C" 已被移除
127.0.0.1:6379> LRANGE mylist 0 -1
1) "A"
2) "B"

# 再次 RPOP,弹出 "B"
127.0.0.1:6379> RPOP mylist
"B"

# 列表为空时,返回 nil
127.0.0.1:6379> RPOP mylist
(nil)

2. BRPOP: 从尾部弹出 (阻塞)

命令BRPOP key [key ...] timeout

功能RPOP阻塞版本 。它会移除并返回列表的最后一个元素。如果所有给定的列表都为空,它会阻塞连接,直到有元素可用或超时。

阻塞。这是其核心特性。

返回值

  • 成功弹出:返回一个包含两个元素的数组 [key, value]
  • 超时:返回 nil

使用场景 :实现消费者,用于任务队列或消息队列,避免轮询。

示例

终端 A (消费者):

bash 复制代码
# 尝试从 mylist 弹出元素,最多阻塞 5 秒
127.0.0.1:6379> BRPOP mylist 5
(等待中...)

终端 B (生产者):

bash 复制代码
# 向 mylist 推送一个新元素
127.0.0.1:6379> LPUSH mylist "New Task"
(integer) 1

终端 A (消费者):

bash 复制代码
# 立即收到响应
1) "mylist"      # 元素来自哪个键
2) "New Task"    # 被弹出的元素值

超时示例:

bash 复制代码
# 5秒内没有任何元素被推入
127.0.0.1:6379> BRPOP emptylist 5
(nil)
(5.04s)

3. LPOP: 从头部弹出 (非阻塞)

命令LPOP key

功能 :移除并返回列表 key第一个元素(即头部元素)。

阻塞

返回值

  • 如果列表存在且非空:返回被弹出的元素。
  • 如果列表不存在或为空:返回 nil

使用场景 :与 RPOP 相反,如果你将列表用作栈,并希望从另一端操作。

示例

bash 复制代码
# 使用之前的列表 ["A", "B"]
127.0.0.1:6379> LRANGE mylist 0 -1
1) "A"
2) "B"

# 执行 LPOP,弹出头部元素 "A"
127.0.0.1:6379> LPOP mylist
"A"

# 列表现在只剩下 ["B"]
127.0.0.1:6379> LRANGE mylist 0 -1
1) "B"

4. BLPOP: 从头部弹出 (阻塞)

命令BLPOP key [key ...] timeout

功能LPOP阻塞版本 。它会移除并返回列表的第一个元素。如果所有给定的列表都为空,它会阻塞连接,直到有元素可用或超时。

阻塞

返回值

  • 成功弹出:返回一个包含两个元素的数组 [key, value]
  • 超时:返回 nil

使用场景 :这是最常用 的队列消费模式。配合 RPUSH(从尾部推入),可以完美实现先进先出 (FIFO) 的队列。

示例 (FIFO 队列)

生产者:

bash 复制代码
# 依次将任务推入队列尾部
127.0.0.1:6379> RPUSH task_queue "Task-1"
(integer) 1
127.0.0.1:6379> RPUSH task_queue "Task-2"
(integer) 2

消费者:

bash 复制代码
# 从队列头部阻塞式地获取任务
127.0.0.1:6379> BLPOP task_queue 0
1) "task_queue"
2) "Task-1"  # 最先推入的任务最先被消费

# 再次执行,获取下一个任务
127.0.0.1:6379> BLPOP task_queue 0
1) "task_queue"
2) "Task-2"

Python代码示例

首先,请确保你已经安装了 redis 库:

bash 复制代码
pip install redis

1. 基础设置

我们先创建一个 Redis 连接,并定义一个辅助函数来清理测试用的 key。

python 复制代码
import redis
import time
import threading

# 创建 Redis 连接
r = redis.Redis(host='localhost', port=6379, decode_responses=True)

def cleanup_keys(*keys):
    """清理测试用的 keys"""
    r.delete(*keys)

2. RPOPLPOP 示例 (非阻塞)

这两个命令会立即返回结果,无论列表是否为空。

python 复制代码
# 清理环境
cleanup_keys('test_list')

# 向列表中添加元素
r.rpush('test_list', 'A', 'B', 'C')
print("初始列表:", r.lrange('test_list', 0, -1))  # ['A', 'B', 'C']

# LPOP: 从头部弹出
head_element = r.lpop('test_list')
print("LPOP 弹出:", head_element)  # A
print("LPOP 后列表:", r.lrange('test_list', 0, -1))  # ['B', 'C']

# RPOP: 从尾部弹出
tail_element = r.rpop('test_list')
print("RPOP 弹出:", tail_element)  # C
print("RPOP 后列表:", r.lrange('test_list', 0, -1))  # ['B']

# 对空列表操作
empty_lpop = r.lpop('empty_list')
empty_rpop = r.rpop('empty_list')
print("空列表 LPOP:", empty_lpop)  # None
print("空列表 RPOP:", empty_rpop)  # None

输出:

复制代码
初始列表: ['A', 'B', 'C']
LPOP 弹出: A
LPOP 后列表: ['B', 'C']
RPOP 弹出: C
RPOP 后列表: ['B']
空列表 LPOP: None
空列表 RPOP: None

3. BLPOP 示例 (阻塞式队列消费者)

BLPOP 是实现 FIFO(先进先出)队列的标准方式。生产者使用 RPUSH,消费者使用 BLPOP

python 复制代码
# 清理环境
cleanup_keys('task_queue')

def producer():
    """模拟生产者,每隔2秒推送一个任务"""
    for i in range(3):
        task = f"Task-{i+1}"
        r.rpush('task_queue', task)
        print(f"[生产者] 推送任务: {task}")
        time.sleep(2)

def blpop_consumer():
    """BLPOP 消费者,阻塞等待任务"""
    print("[BLPOP消费者] 开始等待任务...")
    while True:
        # 阻塞等待,超时时间设为5秒
        result = r.blpop('task_queue', timeout=5)
        if result is None:
            print("[BLPOP消费者] 超时,没有收到新任务,退出。")
            break
        key, value = result
        print(f"[BLPOP消费者] 处理任务: {value} (来自 {key})")

# 启动生产者线程
producer_thread = threading.Thread(target=producer)
producer_thread.start()

# 运行消费者
blpop_consumer()

# 等待生产者线程结束
producer_thread.join()

输出:

复制代码
[BLPOP消费者] 开始等待任务...[生产者] 推送任务: Task-1

[BLPOP消费者] 处理任务: Task-1 (来自 task_queue)
[生产者] 推送任务: Task-2
[BLPOP消费者] 处理任务: Task-2 (来自 task_queue)
[生产者] 推送任务: Task-3[BLPOP消费者] 处理任务: Task-3 (来自 task_queue)

[BLPOP消费者] 超时,没有收到新任务,退出。

4. BRPOP 示例 (阻塞式栈或反向队列)

BRPOP 通常与 LPUSH 配合,也可以实现 FIFO 队列,但更常用于实现 LIFO(后进先出)的栈。

python 复制代码
# 清理环境
cleanup_keys('stack')

def brpop_consumer():
    """BRPOP 消费者,模拟一个栈的弹出操作"""
    print("[BRPOP消费者] 开始从栈中弹出元素...")
    # 先手动压入一些元素
    r.lpush('stack', 'X', 'Y', 'Z')  # 栈: ['Z', 'Y', 'X'] (Z 在顶部/尾部)
    print("当前栈内容:", r.lrange('stack', 0, -1))

    # 弹出3次
    for _ in range(3):
        result = r.brpop('stack', timeout=1)
        if result:
            key, value = result
            print(f"[BRPOP消费者] 弹出: {value}")
    
    # 尝试在空栈上弹出,会超时
    result = r.brpop('stack', timeout=2)
    print("[BRPOP消费者] 空栈弹出结果:", result)  # None

brpop_consumer()

输出:

复制代码
[BRPOP消费者] 开始从栈中弹出元素...
当前栈内容: ['Z', 'Y', 'X']
[BRPOP消费者] 弹出: X
[BRPOP消费者] 弹出: Y
[BRPOP消费者] 弹出: Z
[BRPOP消费者] 空栈弹出结果: None

5. BLPOP / BRPOP 监听多个列表 (优先级队列)

这是阻塞命令的一个强大特性,可以用于实现简单的优先级队列。

python 复制代码
# 清理环境
cleanup_keys('high_priority', 'low_priority')

def multi_list_producer():
    """向高优先级和低优先级队列推送任务"""
    time.sleep(1)  # 稍等一下,让消费者先启动
    r.rpush('low_priority', 'Low-Priority-Task')
    print("[生产者] 推送低优先级任务")
    time.sleep(1)
    r.rpush('high_priority', 'High-Priority-Task')
    print("[生产者] 推送高优先级任务")

def priority_consumer():
    """消费者优先监听高优先级队列"""
    print("[优先级消费者] 启动,优先监听: high_priority, low_priority")
    # BLPOP 会按顺序检查列表
    for _ in range(2):
        result = r.blpop(['high_priority', 'low_priority'], timeout=5)
        if result:
            key, value = result
            print(f"[优先级消费者] 处理任务: {value} (来自 {key})")

# 启动生产者线程
prod_thread = threading.Thread(target=multi_list_producer)
prod_thread.start()

# 运行消费者
priority_consumer()

prod_thread.join()

输出:

复制代码
[优先级消费者] 启动,优先监听: high_priority, low_priority
[生产者] 推送低优先级任务
[优先级消费者] 处理任务: Low-Priority-Task (来自 low_priority)
[生产者] 推送高优先级任务
[优先级消费者] 处理任务: High-Priority-Task (来自 high_priority)

总结对比表

命令 功能 弹出方向 阻塞 典型用途
RPOP 移除并返回最后一个元素 尾部 (Right) 栈 (LIFO)
BRPOP 阻塞式移除并返回最后一个元素 尾部 (Right) 消费者(配合 LPUSH 实现 FIFO)
LPOP 移除并返回第一个元素 头部 (Left) 栈 (LIFO) (另一端)
BLPOP 阻塞式移除并返回第一个元素 头部 (Left) 标准队列消费者 (FIFO) (配合 RPUSH)

关键记忆点

  • L 开头的命令操作头部
  • R 开头的命令操作尾部
  • B 开头的命令是阻塞版本。
  • 标准 FIFO 队列 = RPUSH (生产者) + BLPOP (消费者)
相关推荐
晓得迷路了10 小时前
栗子前端技术周刊第 104 期 - Rspack 1.6、Turborepo 2.6、Chrome 142...
前端·javascript·chrome
倔强的石头_10 小时前
【金仓数据库】ksql 指南(四) —— 创建与管理表(KingbaseES 数据存储核心)
数据库
赵渝强老师10 小时前
【赵渝强老师】TiDB PD集群存储的信息
数据库·mysql·tidb
老纪的技术唠嗑局10 小时前
分库分表MyCat 架构迁移 OceanBase | 百丽核心财务系统迁移经验总结与问题汇总
数据库·架构·oceanbase
程序员三明治12 小时前
选 Redis Stream 还是传统 MQ?队列选型全攻略(适用场景、优缺点与实践建议)
java·redis·后端·缓存·rocketmq·stream·队列
xrkhy16 小时前
微服务之ShardingSphere
数据库·微服务·oracle
JIngJaneIL17 小时前
停车场管理|停车预约管理|基于Springboot的停车场管理系统设计与实现(源码+数据库+文档)
java·数据库·spring boot·后端·论文·毕设·停车场管理系统
煎蛋学姐17 小时前
SSM儿童福利院管理系统ys9w2d07(程序+源码+数据库+调试部署+开发环境)带论文文档1万字以上,文末可获取,系统界面在最后面。
数据库·ssm 框架·儿童福利院管理系统
sg_knight17 小时前
MySQL 空间索引(SPATIAL)详解:地理位置数据的高效查询利器
数据库·mysql·database·索引·关系型数据库·空间索引·spatial
梦子yumeko18 小时前
第五章Langchain4j之基于内存和redis实现聊天持久化
数据库·redis·缓存