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 (消费者)
相关推荐
数据和云4 小时前
从Databricks和Supabase看AI时代的中国数据库启示
数据库·人工智能
我科绝伦(Huanhuan Zhou)4 小时前
Oracle ADRCI工具全面使用指南:从基础到故障诊断实战
数据库·oracle
数据库生产实战4 小时前
Oracle LOB使用入门和简单使用,提供学习用的测试用例!
数据库·学习·oracle
武子康4 小时前
Java-144 深入浅出 MongoDB BSON详解:MongoDB核心存储格式与JSON的区别与应用场景
java·开发语言·数据库·mongodb·性能优化·json·bjson
Raymond运维4 小时前
MySQL包安装 -- SUSE系列(SUSE资源库安装MySQL)
linux·运维·数据库·mysql
高山上有一只小老虎4 小时前
如何在DBeaver中配置高斯数据库的连接
数据库
云飞云共享云桌面4 小时前
东莞精密机械制造工厂如何10个SolidWorks共用一台服务器资源
java·运维·服务器·网络·数据库·电脑·制造
ActionTech4 小时前
2025 年 9 月《大模型 SQL 能力排行榜》发布,新增 Kimi K2 最新版测评!
数据库·sql·ai·oracle
lang201509285 小时前
掌握MyBatis Java API:高效操作数据库
java·数据库·mybatis