Redis基本数据类型:List

72 阅读4分钟

List

一、List 类型概述

Redis 的 List 是一个简单的 字符串列表,按照插入顺序排序。你可以从列表的 头部(左边)  或 尾部(右边)  添加元素。

这使得 Redis 的 List 可以被用作:

  • 栈(Stack)LPUSH + LPOP 或 RPUSH + RPOP
  • 队列(Queue)LPUSH + RPOP 或 RPUSH + LPOP
  • 阻塞队列(Blocking Queue)BLPOP / BRPOP

List 的最大长度为 2³² - 1 个元素(超过 40 亿个元素)。

二、基本方法

  1. 添加获取元素
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"
  1. 在指定位置插入元素
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"
  1. 修改元素
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
  1. 修剪列表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"
  1. 查找元素位置 (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
  1. 元素移动(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 在底层实现上并 不是 一个简单的链表或数组,而是根据元素数量和大小,智能地使用两种数据结构:

  1. ziplist(压缩列表)
    • 当列表元素个数较少,且每个元素都是小整数或短字符串时使用。
    • 它是一块连续的内存,非常节省内存。
  2. linkedlist(快速链表,quicklist
    • 在较新版本的 Redis 中,List 的实现是 quicklist
    • quicklist 是 ziplist 和双向链表的结合体,它将多个 ziplist 通过链表连接起来,在空间效率和操作效率之间取得了很好的平衡。

你可以使用 OBJECT ENCODING key 命令来查看一个 List 的内部编码。

四、应用场景

  1. 消息队列(最经典)
    • 生产者 使用 LPUSH 将任务放入列表。
    • 消费者 使用 RPOP(或 BRPOP)从列表中取出任务进行处理。
    • 使用 BRPOP 可以实现阻塞式获取,避免消费者不停地轮询。
  2. 栈(LIFO,后进先出)
    • LPUSH + LPOP 或 RPUSH + RPOP
  3. 文章时间线、社交网络动态(Timeline)
    • 用户发布新动态时,LPUSH 到自己的动态列表。
    • 获取最新动态时,使用 LRANGE key 0 9 来获取最新的 10 条。
  4. 记录前N个最新操作
    • 使用 LPUSH 加入新操作,然后用 LTRIM key 0 N-1 来只保留最近的 N 个操作。
  5. 安全队列
    • 使用 RPOPLPUSH 可以在取出任务进行处理的同时,将这个任务放入另一个“进行中”的列表。如果消费者崩溃,可以从“进行中”列表恢复任务,避免任务丢失。