List
一、List 类型概述
Redis 的 List 是一个简单的 字符串列表,按照插入顺序排序。你可以从列表的 头部(左边) 或 尾部(右边) 添加元素。
这使得 Redis 的 List 可以被用作:
- 栈(Stack) :
LPUSH+LPOP或RPUSH+RPOP - 队列(Queue) :
LPUSH+RPOP或RPUSH+LPOP - 阻塞队列(Blocking Queue) :
BLPOP/BRPOP
List 的最大长度为 2³² - 1 个元素(超过 40 亿个元素)。
二、基本方法
- 添加获取元素
127.0.0.1:6379> lpush name o #将一个或多个值插入到列表头部
(integer) 1
127.0.0.1:6379> lpush name a
(integer) 2
127.0.0.1:6379> lpush name i
(integer) 3
127.0.0.1:6379> lpush name x
(integer) 4
#获取列表中指定范围内的元素。(0,-1)就是获取全部 0表示第一个元素,-1表示最后一个元素,-2表示倒数第二个,
127.0.0.1:6379> lrange name 0 -1
1) "x"
2) "i"
3) "a"
4) "o"
127.0.0.1:6379> lindex name 2 #通过索引获取value
"a"
127.0.0.1:6379> llen name #获取list的长度
(integer) 4
127.0.0.1:6379> rpush name z h a n g #将一个或多个值插入到列表尾部
(integer) 9
127.0.0.1:6379> lrange name 0 -1
1) "x"
2) "i"
3) "a"
4) "o"
5) "z"
6) "h"
7) "a"
8) "n"
9) "g"
- 在指定位置插入元素
127.0.0.1:6379> linsert name before x hhh #在列表的某个已有元素(pivot)的前插入新元素
(integer) 10
127.0.0.1:6379> lrange name 0 -1
1) "hhh"
2) "x"
3) "i"
4) "a"
5) "o"
6) "z"
7) "h"
8) "a"
9) "n"
10) "g"
127.0.0.1:6379> linsert name after g hhh #在列表的某个已有元素(pivot)的后插入新元素
(integer) 11
127.0.0.1:6379> linsert name after hhh hello #如果出现多个已有元素,则选择偏头部的元素
(integer) 12
127.0.0.1:6379> lrange name 0 -1
1) "hhh"
2) "hello"
3) "x"
4) "i"
5) "a"
6) "o"
7) "z"
8) "h"
9) "a"
10) "n"
11) "g"
12) "hhh"
127.0.0.1:6379>
3.弹出/删除元素
127.0.0.1:6379> lpop name #头部弹出
"hhh" #弹出内容
127.0.0.1:6379> lrange name 0 -1
1) "hello"
2) "x"
3) "i"
4) "a"
5) "o"
6) "z"
7) "h"
8) "a"
9) "n"
10) "g"
11) "hhh"
127.0.0.1:6379> rpop name #尾部弹出
"hhh" #弹出内容
127.0.0.1:6379> lrange name 0 -1
1) "hello"
2) "x"
3) "i"
4) "a"
5) "o"
6) "z"
7) "h"
8) "a"
9) "n"
10) "g"
127.0.0.1:6379> rpop name 5 #批量弹出
1) "g"
2) "n"
3) "a"
4) "h"
5) "z"
127.0.0.1:6379> lrange name 0 -1
1) "hello"
2) "x"
3) "i"
4) "a"
5) "o"
#根据参数count的值,移除列表中与参数value相等的元素。
#count > 0:从表头开始向表尾搜索,移除与value相等的元素,数量为count。
#count < 0:从表尾开始向表头搜索,移除与value相等的元素,数量为count的绝对值。
#count = 0:移除表中所有与value相等的值。
127.0.0.1:6379> lrem name 0 hello
(integer) 1
127.0.0.1:6379> lrange name 0 -1
1) "x"
2) "i"
3) "a"
4) "o"
- 修改元素
127.0.0.1:6379> lset name 0 hello #修改第零号元素
OK
127.0.0.1:6379> lrange name 0 -1
1) "hello"
2) "i"
3) "a"
4) "o"
127.0.0.1:6379> lset name 4 hhh #如果没有元素会报错
(error) ERR index out of range
- 修剪列表LTRIM,保留指定范围内的元素,删除范围外的所有元素
# 只保留列表的前10个元素,实现"固定长度列表"
127.0.0.1:6379> LPUSH logs "log5" "log4" "log3" "log2" "log1"
(integer) 5
127.0.0.1:6379> LTRIM logs 0 2 # 只保留前3个元素
OK
127.0.0.1:6379> LRANGE logs 0 -1
1) "log1"
2) "log2"
3) "log3"
- 查找元素位置 (Redis 6.0.6+)
127.0.0.1:6379> RPUSH list "a" "b" "c" "b" "d"
(integer) 5
127.0.0.1:6379> LPOS list "b" # 返回第一个"b"的位置
(integer) 1
127.0.0.1:6379> LPOS list "b" RANK 2 # 返回第二个"b"的位置
(integer) 3
127.0.0.1:6379> LPOS list "b" COUNT 2 # 返回前2个"b"的位置
1) (integer) 1
2) (integer) 3
- 元素移动(Redis 6.2+)
127.0.0.1:6379> LMOVE source destination LEFT RIGHT # 从source左边弹出,推到destination右边
127.0.0.1:6379> LMOVE source destination RIGHT LEFT # 从source右边弹出,推到destination左边
三、内部实现
Redis 的 List 在底层实现上并 不是 一个简单的链表或数组,而是根据元素数量和大小,智能地使用两种数据结构:
ziplist(压缩列表) :- 当列表元素个数较少,且每个元素都是小整数或短字符串时使用。
- 它是一块连续的内存,非常节省内存。
linkedlist(快速链表,quicklist) :- 在较新版本的 Redis 中,List 的实现是
quicklist。 quicklist是ziplist和双向链表的结合体,它将多个ziplist通过链表连接起来,在空间效率和操作效率之间取得了很好的平衡。
- 在较新版本的 Redis 中,List 的实现是
你可以使用 OBJECT ENCODING key 命令来查看一个 List 的内部编码。
四、应用场景
- 消息队列(最经典)
- 生产者 使用
LPUSH将任务放入列表。 - 消费者 使用
RPOP(或BRPOP)从列表中取出任务进行处理。 - 使用
BRPOP可以实现阻塞式获取,避免消费者不停地轮询。
- 生产者 使用
- 栈(LIFO,后进先出)
LPUSH+LPOP或RPUSH+RPOP
- 文章时间线、社交网络动态(Timeline)
- 用户发布新动态时,
LPUSH到自己的动态列表。 - 获取最新动态时,使用
LRANGE key 0 9来获取最新的 10 条。
- 用户发布新动态时,
- 记录前N个最新操作
- 使用
LPUSH加入新操作,然后用LTRIM key 0 N-1来只保留最近的 N 个操作。
- 使用
- 安全队列
- 使用
RPOPLPUSH可以在取出任务进行处理的同时,将这个任务放入另一个“进行中”的列表。如果消费者崩溃,可以从“进行中”列表恢复任务,避免任务丢失。
- 使用