Redis 列表(下)
本课学习列表的查询、修改、插入等高级操作。
LINDEX:按索引获取元素
LINDEX根据索引获取列表中的元素,不删除元素。
基本用法
REDIS
LPUSH mylist "one" "two" "three" "four" "five"
# 获取第一个元素(索引0)
LINDEX mylist 0
"five"
# 获取第二个元素
LINDEX mylist 1
"four"
# 获取最后一个元素
LINDEX mylist -1
"one"
# 获取倒数第二个元素
LINDEX mylist -2
"two"
索引超出范围
REDIS
# 索引超出范围返回nil
LINDEX mylist 10
(nil)
LINDEX mylist -10
(nil)
💡 性能: LINDEX时间复杂度O(N),N是索引的绝对值。访问头部和尾部快,访问中间慢。
LSET:按索引设置元素
LSET修改指定索引位置的元素。
基本用法
REDIS
LRANGE mylist 0 -1
1) "five"
2) "four"
3) "three"
4) "two"
5) "one"
# 修改第一个元素
LSET mylist 0 "FIRST"
OK
# 修改最后一个元素
LSET mylist -1 "LAST"
OK
LRANGE mylist 0 -1
1) "FIRST"
2) "four"
3) "three"
4) "two"
5) "LAST"
索引超出范围
REDIS
LSET mylist 10 "value"
(error) ERR index out of range
⚠️ 注意: LSET只能修改已存在的索引,不能用来插入新元素。
LINSERT:在指定元素前后插入
LINSERT在指定元素的前面或后面插入新元素。
LINSERT BEFORE:在前面插入
REDIS
LRANGE mylist 0 -1
1) "FIRST"
2) "four"
3) "three"
4) "two"
5) "LAST"
# 在"three"前面插入"BEFORE_THREE"
LINSERT mylist BEFORE "three" "BEFORE_THREE"
(integer) 6 # 返回列表长度
LRANGE mylist 0 -1
1) "FIRST"
2) "four"
3) "BEFORE_THREE"
4) "three"
5) "two"
6) "LAST"
LINSERT AFTER:在后面插入
REDIS
# 在"three"后面插入"AFTER_THREE"
LINSERT mylist AFTER "three" "AFTER_THREE"
(integer) 7
LRANGE mylist 0 -1
1) "FIRST"
2) "four"
3) "BEFORE_THREE"
4) "three"
5) "AFTER_THREE"
6) "two"
7) "LAST"
元素不存在
REDIS
# 指定的pivot元素不存在
LINSERT mylist BEFORE "notexist" "value"
(integer) -1 # 返回-1表示未找到pivot
⚠️ 性能警告: LINSERT时间复杂度O(N),需要遍历列表查找pivot。列表很长时会很慢。
LREM:删除指定值的元素
LREM删除列表中等于指定值的元素。
基本用法
REDIS
LPUSH mylist "a" "b" "a" "c" "a" "d"
LRANGE mylist 0 -1
1) "d"
2) "a"
3) "c"
4) "a"
5) "b"
6) "a"
# 从头部开始删除2个"a"
LREM mylist 2 "a"
(integer) 2 # 删除了2个
LRANGE mylist 0 -1
1) "d"
2) "c"
3) "b"
4) "a"
参数说明
| 参数 | 说明 |
|---|---|
| count > 0 | 从头部开始删除count个 |
| count < 0 | 从尾部开始删除abs(count)个 |
| count = 0 | 删除所有匹配的元素 |
从尾部删除
REDIS
LPUSH mylist "a" "b" "a" "c" "a"
# 从尾部开始删除1个"a"
LREM mylist -1 "a"
(integer) 1
删除所有匹配元素
REDIS
LPUSH mylist "a" "b" "a" "c" "a"
# 删除所有"a"
LREM mylist 0 "a"
(integer) 3
LRANGE mylist 0 -1
1) "c"
2) "b"
RPOPLPUSH:移动元素
RPOPLPUSH将一个列表的尾部元素弹出,并插入到另一个列表的头部。
基本用法
REDIS
# 源列表
LPUSH source "one" "two" "three"
# 目标列表
LPUSH destination "a" "b"
# 将source的尾部元素移动到destination的头部
RPOPLPUSH source destination
"one" # 返回移动的元素
LRANGE source 0 -1
1) "three"
2) "two"
LRANGE destination 0 -1
1) "one"
2) "a"
3) "b"
应用:循环队列
REDIS
# 将任务从队列尾部移回队列头部,实现循环处理
RPOPLPUSH queue:task queue:task
应用:备份队列
REDIS
# 处理任务时,将任务移到备份队列
RPOPLPUSH queue:task queue:backup
# 如果处理失败,可以从备份队列恢复
BRPOPLPUSH:阻塞式移动
BRPOPLPUSH是RPOPLPUSH的阻塞版本。
REDIS
# 如果source列表为空,阻塞等待最多10秒
BRPOPLPUSH source destination 10
💡 用途: BRPOPLPUSH常用于可靠的消息队列,确保消息不丢失。
LMOVE:移动元素(Redis 6.2+)
LMOVE是RPOPLPUSH的通用版本,可以指定从哪里弹出和插入到哪里。
REDIS
# 从source的右侧弹出,插入到destination的左侧
LMOVE source destination RIGHT LEFT
# 从source的左侧弹出,插入到destination的右侧
LMOVE source destination LEFT RIGHT
列表高级应用
场景1:分页查询
REDIS
# 文章列表
LPUSH articles "article:1" "article:2" ... "article:100"
# 第1页(每页10条)
LRANGE articles 0 9
# 第2页
LRANGE articles 10 19
# 第3页
LRANGE articles 20 29
场景2:最近联系人
REDIS
# 添加联系人到最近列表
LPUSH contacts:user:1 "user:2"
# 如果已存在,先删除再添加(移到最前面)
LREM contacts:user:1 0 "user:2"
LPUSH contacts:user:1 "user:2"
# 获取最近10个联系人
LRANGE contacts:user:1 0 9
场景3:轮询调度
REDIS
# 任务列表
LPUSH tasks "task1" "task2" "task3"
# 轮询处理:取出任务,处理后放回队尾
RPOPLPUSH tasks tasks
"task1" # 处理task1
# 下次轮询会处理task2
场景4:限制长度的日志
REDIS
# 添加日志
LPUSH log:app "log message"
# 保持最近1000条日志
LTRIM log:app 0 999
# 查看最近日志
LRANGE log:app 0 99
列表性能优化
1. 避免中间操作
REDIS
# ❌ 性能差:在列表中间插入或删除
LINSERT mylist BEFORE "middle" "new"
LREM mylist 1 "middle"
# ✅ 性能好:只在头尾操作
LPUSH mylist "new"
LPOP mylist
2. 避免大列表
REDIS
# ❌ 列表太大
LLEN mylist
(integer) 1000000
# ✅ 使用LTRIM限制长度
LTRIM mylist 0 9999
3. 使用LRANGE分批获取
REDIS
# ❌ 一次性获取大量元素
LRANGE mylist 0 -1
# ✅ 分批获取
LRANGE mylist 0 99
LRANGE mylist 100 199
4. 选择合适的数据结构
| 需求 | 推荐数据结构 |
|---|---|
| 队列/栈 | List |
| 最新N条 | List + LTRIM |
| 分页查询 | List + LRANGE |
| 去重列表 | Set 或 Sorted Set |
| 带权重排序 | Sorted Set |
列表限制
1. 查找元素慢
REDIS
# 查找特定值需要遍历整个列表
LINDEX mylist 500000 # O(N)操作,很慢
2. 中间操作慢
REDIS
# 在中间插入或删除需要移动元素
LINSERT mylist BEFORE "middle" "new" # O(N)
3. 无法按值查找索引
Redis没有提供按值查找索引的命令,需要应用层自己实现。
❓ 常见问题
Q LINDEX和LRANGE的区别?
A LINDEX返回单个元素,LRANGE返回元素列表。LINDEX mylist 0 等价于 LRANGE mylist 0 0。
Q 如何在列表中间插入元素?
A 使用LINSERT命令,但性能较差。如果需要频繁中间操作,考虑使用其他数据结构。
Q RPOPLPUSH是原子操作吗?
A 是的。RPOPLPUSH是原子操作,要么全部成功,要么全部失败。
Q 如何实现优先级队列?
A 使用多个列表(如queue:high, queue:low),先检查高优先级队列,再检查低优先级队列。
Q 列表和集合的区别?
A 列表有序可重复,集合无序不重复。需要顺序用列表,需要去重用集合。
📖 小节
- LINDEX按索引获取元素,LSET按索引设置元素
- LINSERT在指定元素前后插入,性能较差O(N)
- LREM删除指定值的元素,可控制删除数量和方向
- RPOPLPUSH移动元素,用于循环队列和备份队列
- BRPOPLPUSH阻塞式移动,用于可靠消息队列
- 列表高级应用:分页、最近联系人、轮询调度
- 性能优化:避免中间操作、限制列表长度、分批获取
📝 作业
- 索引操作: 创建一个列表,使用LINDEX获取不同位置的元素,使用LSET修改元素
- 插入删除: 使用LINSERT在列表中间插入元素,使用LREM删除指定值的元素
- 元素移动: 使用RPOPLPUSH实现一个循环队列,模拟任务轮询
- 分页查询: 创建一个包含20个元素的列表,实现每页5条的分页查询
下一课
下一课我们将学习 Redis 集合(上),学习集合的基本操作。



