Redis 列表
提示
来自deepseek解释
原文链接:https://redis.io/docs/latest/develop/data-types/lists/
代码示例图例
以下代码示例展示了在不同编程语言和客户端库中执行相同操作的方式。本页仅展示 Java 客户端的示例(同步 Jedis、异步 Lettuce 和响应式 Lettuce)。
列表命令摘要
本组共 22 条命令:
| 命令 | 摘要 | 复杂度 | 起始版本 |
|---|---|---|---|
| BLMOVE | 从列表弹出元素并推入另一列表,返回该元素;若无可用元素则阻塞。删除空列表。 | O(1) | 6.2.0 |
| BLMPOP | 从多个列表之一弹出第一个元素;若无可用元素则阻塞。删除空列表。 | O(N+M) | 7.0.0 |
| BLPOP | 移除并返回列表第一个元素;若无可用元素则阻塞。删除空列表。 | O(N) | 2.0.0 |
| BRPOP | 移除并返回列表最后一个元素;若无可用元素则阻塞。删除空列表。 | O(N) | 2.0.0 |
| BRPOPLPUSH | 从列表弹出元素并推入另一列表,返回该元素;若无可用元素则阻塞。删除空列表。 | O(1) | 2.2.0 |
| LINDEX | 按索引获取列表中的元素。 | O(N) | 1.0.0 |
| LINSERT | 在列表中的元素前或后插入元素。 | O(N) | 2.2.0 |
| LLEN | 返回列表长度。 | O(1) | 1.0.0 |
| LMOVE | 弹出列表元素并推入另一列表,返回该元素。删除空列表。 | O(1) | 6.2.0 |
| LMPOP | 从列表中移除并返回多个元素。删除空列表。 | O(N+M) | 7.0.0 |
| LPOP | 移除并返回列表前几个元素。删除空列表。 | O(N) | 1.0.0 |
| LPOS | 返回列表中匹配元素的索引。 | O(N) | 6.0.6 |
| LPUSH | 在列表头部插入一个或多个元素。若键不存在则创建。 | O(1) | 1.0.0 |
| LPUSHX | 仅在列表存在时,在头部插入元素。 | O(1) | 2.2.0 |
| LRANGE | 返回列表中指定范围的元素。 | O(S+N) | 1.0.0 |
| LREM | 从列表中移除元素。若为空则删除键。 | O(N+M) | 1.0.0 |
| LSET | 按索引设置列表元素的值。 | O(N) | 1.0.0 |
| LTRIM | 从列表两端移除元素,仅保留指定范围。若全部移除则删除键。 | O(N) | 1.0.0 |
| RPOP | 移除并返回列表末尾元素。删除空列表。 | O(N) | 1.0.0 |
| RPOPLPUSH | 弹出列表末尾元素并推入另一列表,返回该元素。删除空列表。 | O(1) | 1.2.0 |
| RPUSH | 在列表尾部插入一个或多个元素。若键不存在则创建。 | O(1) | 1.0.0 |
| RPUSHX | 仅在列表存在时,在尾部插入元素。 | O(1) | 2.2.0 |
Redis 列表是字符串值的链表。常用于:
- 实现栈和队列。
- 为后台工作系统构建队列管理。
示例
- 将列表用作队列(先进先出):
队列模式:使用 LPUSH 添加项,RPOP 按 FIFO 顺序移除,用于按添加顺序处理项
命令: LPUSH, RPOP
Java(异步 - Lettuce)
CompletableFuture<Void> queue = asyncCommands.lpush("bikes:repairs", "bike:1").thenCompose(res1 -> {
System.out.println(res1); // >>> 1
return asyncCommands.lpush("bikes:repairs", "bike:2");
}).thenCompose(res2 -> {
System.out.println(res2); // >>> 2
return asyncCommands.rpop("bikes:repairs");
}).thenCompose(res3 -> {
System.out.println(res3); // >>> bike:1
return asyncCommands.rpop("bikes:repairs");
})
.thenAccept(System.out::println) // >>> bike:2
.toCompletableFuture();Java(同步 - Jedis)
long res1 = jedis.lpush("bikes:repairs", "bike:1");
System.out.println(res1); // >>> 1
long res2 = jedis.lpush("bikes:repairs", "bike:2");
System.out.println(res2); // >>> 2
String res3 = jedis.rpop("bikes:repairs");
System.out.println(res3); // >>> bike:1
String res4 = jedis.rpop("bikes:repairs");
System.out.println(res4); // >>> bike:2- 将列表用作栈(先进后出):
栈模式:使用 LPUSH 添加项,LPOP 按 LIFO 顺序移除,用于逆序处理项
命令: LPUSH, LPOP
Java(异步 - Lettuce)
CompletableFuture<Void> stack = asyncCommands.lpush("bikes:repairs", "bike:1").thenCompose(res4 -> {
System.out.println(res4); // >>> 1
return asyncCommands.lpush("bikes:repairs", "bike:2");
}).thenCompose(res5 -> {
System.out.println(res5); // >>> 2
return asyncCommands.lpop("bikes:repairs");
}).thenCompose(res6 -> {
System.out.println(res6); // >>> bike:2
return asyncCommands.lpop("bikes:repairs");
})
.thenAccept(System.out::println) // >>> bike:1
.toCompletableFuture();Java(同步 - Jedis)
long res5 = jedis.lpush("bikes:repairs", "bike:1");
System.out.println(res5); // >>> 1
long res6 = jedis.lpush("bikes:repairs", "bike:2");
System.out.println(res6); // >>> 2
String res7 = jedis.lpop("bikes:repairs");
System.out.println(res7); // >>> bike:2
String res8 = jedis.lpop("bikes:repairs");
System.out.println(res8); // >>> bike:1- 获取列表长度:
基础:使用 LLEN 获取列表长度
命令: LLEN
Java(异步 - Lettuce)
CompletableFuture<Void> llen = asyncCommands.llen("bikes:repairs")
.thenAccept(System.out::println) // >>> 0
.toCompletableFuture();Java(同步 - Jedis)
long res9 = jedis.llen("bikes:repairs");
System.out.println(res9); // >>> 0- 原子地将一个列表中的元素弹出并推入另一个列表:
原子转移:使用 LMOVE 在单次操作中移动元素,避免竞争条件
命令: LPUSH, LMOVE, LRANGE
Java(异步 - Lettuce)
CompletableFuture<Void> lmovelrange = asyncCommands.lpush("bikes:repairs", "bike:1").thenCompose(res7 -> {
System.out.println(res7); // >>> 1
return asyncCommands.lpush("bikes:repairs", "bike:2");
}).thenCompose(res8 -> {
System.out.println(res8); // >>> 2
return asyncCommands.lmove("bikes:repairs", "bikes:finished", LMoveArgs.Builder.leftLeft());
}).thenCompose(res9 -> {
System.out.println(res9); // >>> bike:2
return asyncCommands.lrange("bikes:repairs", 0, -1);
}).thenCompose(res10 -> {
System.out.println(res10); // >>> [bike:1]
return asyncCommands.lrange("bikes:finished", 0, -1);
})
.thenAccept(System.out::println) // >>> [bike:2]
.toCompletableFuture();Java(同步 - Jedis)
long res10 = jedis.lpush("bikes:repairs", "bike:1");
System.out.println(res10); // >>> 1
long res11 = jedis.lpush("bikes:repairs", "bike:2");
System.out.println(res11); // >>> 2
String res12 = jedis.lmove("bikes:repairs", "bikes:finished", ListDirection.LEFT, ListDirection.LEFT);
System.out.println(res12); // >>> bike:2
List<String> res13 = jedis.lrange("bikes:repairs", 0, -1);
System.out.println(res13); // >>> [bike:1]
List<String> res14 = jedis.lrange("bikes:finished", 0, -1);
System.out.println(res14); // >>> [bike:2]- 使用
LTRIM限制列表长度:
定长列表:使用 LTRIM 仅保留指定范围元素,维护固定大小
命令: DEL, RPUSH, LTRIM, LRANGE
Java(异步 - Lettuce)
CompletableFuture<Void> ltrim1 = asyncCommands
.lpush("bikes:repairs", "bike:1", "bike:2", "bike:3", "bike:4", "bike:5").thenCompose(res37 -> {
System.out.println(res37); // >>> 5
return asyncCommands.ltrim("bikes:repairs", 0, 2);
}).thenCompose(res38 -> {
System.out.println(res38); // >>> OK
return asyncCommands.lrange("bikes:repairs", 0, -1);
})
.thenAccept(System.out::println) // >>> [bike:5, bike:4, bike:3]
.toCompletableFuture();Java(同步 - Jedis)
jedis.del("bikes:repairs");
long res48 = jedis.lpush("bikes:repairs", "bike:1", "bike:2", "bike:3", "bike:4", "bike:5");
System.out.println(res48); // >>> 5
String res49 = jedis.ltrim("bikes:repairs", 0, 2);
System.out.println(res49); // >>> OK
List<String> res50 = jedis.lrange("bikes:repairs", 0, -1);
System.out.println(res50); // >>> [bike:5, bike:4, bike:3]什么是列表?
为了更好地解释列表数据类型,最好先从一点理论开始,因为 List 这个术语在 IT 领域常常被误用。例如,“Python 列表”并不是名称所暗示的(链表),而是数组(实际上 Ruby 中将相同的数据类型称为 Array)。
从一般角度来看,列表只是一个有序元素的序列:10,20,1,2,3 是一个列表。但使用数组实现的列表属性与使用链表实现的列表属性有很大不同。
Redis 列表通过链表实现。这意味着即使列表中有数百万个元素,在列表头部或尾部添加新元素的操作也是常数时间。使用 LPUSH 命令向一个包含十个元素的列表头部添加新元素,与向包含千万个元素的列表头部添加元素的速度相同。
缺点是:在数组实现的列表中,按索引访问元素非常快(常数时间索引访问),而在链表实现的列表中则不那么快(操作所需工作量与访问元素的索引成正比)。
Redis 列表使用链表实现,因为对于数据库系统来说,能够以非常快的方式向非常长的列表添加元素至关重要。另一个强大优势是,Redis 列表可以在常数时间内截取固定长度。
当需要快速访问大型集合中间部分时,可以使用另一种数据结构——有序集合。有序集合在有序集合教程页面中有介绍。
Redis 列表入门
LPUSH 命令向列表的左侧(头部)添加新元素,RPUSH 命令向列表的右侧(尾部)添加新元素。最后,LRANGE 命令提取列表中的元素范围:
基础:使用 LPUSH(左/头)和 RPUSH(右/尾)在两端添加元素
命令: DEL, RPUSH, LPUSH, LRANGE
Java(异步 - Lettuce)
CompletableFuture<Void> lpushrpush = asyncCommands.rpush("bikes:repairs", "bike:1").thenCompose(res11 -> {
System.out.println(res11); // >>> 1
return asyncCommands.rpush("bikes:repairs", "bike:2");
}).thenCompose(res12 -> {
System.out.println(res12); // >>> 2
return asyncCommands.lpush("bikes:repairs", "bike:important_bike");
}).thenCompose(res13 -> {
System.out.println(res13); // >>> 3
return asyncCommands.lrange("bikes:repairs", 0, -1);
})
.thenAccept(System.out::println)
// >>> [bike:important_bike, bike:1, bike:2]
.toCompletableFuture();Java(同步 - Jedis)
jedis.del("bikes:repairs");
long res15 = jedis.rpush("bikes:repairs", "bike:1");
System.out.println(res15); // >>> 1
long res16 = jedis.rpush("bikes:repairs", "bike:2");
System.out.println(res16); // >>> 2
long res17 = jedis.lpush("bikes:repairs", "bike:important_bike");
System.out.println(res17); // >>> 3
List<String> res18 = jedis.lrange("bikes:repairs", 0, -1);
System.out.println(res18); // >>> [bike:important_bike, bike:1, bike:2]注意,LRANGE 接受两个索引,即要返回的范围的第一个和最后一个元素。两个索引都可以为负数,告诉 Redis 从末尾开始计数:-1 是最后一个元素,-2 是倒数第二个,依此类推。
如您所见,RPUSH 在列表右侧追加元素,而最后的 LPUSH 在左侧追加元素。
这两个命令都是变参命令,意味着您可以在单次调用中向列表推送多个元素:
变参操作:单次命令添加多个元素,减少往返次数
命令: DEL, RPUSH, LPUSH, LRANGE
Java(异步 - Lettuce)
CompletableFuture<Void> variadic = asyncCommands.rpush("bikes:repairs", "bike:1", "bike:2", "bike:3")
.thenCompose(res14 -> {
System.out.println(res14); // >>> 3
return asyncCommands.lpush("bikes:repairs", "bike:important_bike", "bike:very_important_bike");
}).thenCompose(res15 -> {
System.out.println(res15); // >>> 5
return asyncCommands.lrange("bikes:repairs", 0, -1);
})
.thenAccept(System.out::println)
// >>> [bike:very_important_bike, bike:important_bike, bike:1, bike:2, bike:3]
.toCompletableFuture();Java(同步 - Jedis)
jedis.del("bikes:repairs");
long res19 = jedis.rpush("bikes:repairs", "bike:1", "bike:2", "bike:3");
System.out.println(res19); // >>> 3
long res20 = jedis.lpush("bikes:repairs", "bike:important_bike", "bike:very_important_bike");
System.out.println(res20); // >>> 5
List<String> res21 = jedis.lrange("bikes:repairs", 0, -1);
System.out.println(res21); // >>> [bike:very_important_bike, bike:important_bike, bike:1, bike:2, bike:3]Redis 列表的一个重要操作是 弹出元素。弹出元素是指从列表中检索元素并同时将其删除的操作。您可以从左侧和右侧弹出元素,类似于可以从列表两侧推入元素。我们将添加三个元素并弹出三个元素,因此在这组命令结束后列表为空,没有更多元素可弹出:
基础:使用 LPOP(左/头)和 RPOP(右/尾)移除并获取两端元素
命令: DEL, RPUSH, RPOP, LPOP
Java(异步 - Lettuce)
CompletableFuture<Void> lpoprpop = asyncCommands.rpush("bikes:repairs", "bike:1", "bike:2", "bike:3")
.thenCompose(res16 -> {
System.out.println(res16); // >>> 3
return asyncCommands.rpop("bikes:repairs");
}).thenCompose(res17 -> {
System.out.println(res17); // >>> bike:3
return asyncCommands.lpop("bikes:repairs");
}).thenCompose(res18 -> {
System.out.println(res18); // >>> bike:1
return asyncCommands.rpop("bikes:repairs");
}).thenCompose(res19 -> {
System.out.println(res19); // >>> bike:2
return asyncCommands.rpop("bikes:repairs");
})
.thenAccept(System.out::println) // >>> null
.toCompletableFuture();Java(同步 - Jedis)
jedis.del("bikes:repairs");
long res22 = jedis.rpush("bikes:repairs", "bike:1", "bike:2", "bike:3");
System.out.println(res22); // >>> 3
String res23 = jedis.rpop("bikes:repairs");
System.out.println(res23); // >>> bike:3
String res24 = jedis.lpop("bikes:repairs");
System.out.println(res24); // >>> bike:1
String res25 = jedis.rpop("bikes:repairs");
System.out.println(res25); // >>> bike:2
String res26 = jedis.rpop("bikes:repairs");
System.out.println(res26); // >>> nullRedis 返回 NULL 值表示列表中没有元素。
列表的常见使用场景
列表适用于多种任务,两个典型代表用例如下:
- 记录用户在社交网络中发布的最新更新。
- 进程间通信,使用生产者-消费者模式,生产者将项推入列表,消费者(通常是 worker)消费这些项并执行操作。Redis 提供了特殊的列表命令,使此用例更加可靠和高效。
例如,流行的 Ruby 库 resque 和 sidekiq 都在底层使用 Redis 列表来实现后台任务。
流行的 Twitter 社交网络将用户的最新推文放入 Redis 列表。
逐步描述一个常见用例:假设您的主页显示照片分享社交网络中用户发布的最新照片,您希望加快访问速度。
- 每当用户发布新照片,我们使用
LPUSH将其 ID 添加到列表中。 - 当用户访问主页时,我们使用
LRANGE 0 9获取最新发布的 10 个项目。
定长列表
在许多用例中,我们只想使用列表存储 最新项目,无论是社交网络更新、日志还是其他内容。
Redis 允许我们将列表用作定长集合,仅记住最新的 N 个项目,并使用 LTRIM 命令丢弃所有最旧的项目。
LTRIM 命令类似于 LRANGE,但 它不是显示指定范围的元素,而是将该范围设置为列表的新值。给定范围之外的所有元素都会被移除。
例如,如果您在维修列表末尾添加自行车,但只想关注列表中最旧的 3 辆:
定长列表:使用正索引的 LTRIM 保留从开头开始的元素范围
命令: DEL, RPUSH, LTRIM, LRANGE
Java(异步 - Lettuce)
CompletableFuture<Void> ltrim = asyncCommands
.lpush("bikes:repairs", "bike:1", "bike:2", "bike:3", "bike:4", "bike:5").thenCompose(res20 -> {
System.out.println(res20); // >>> 5
return asyncCommands.ltrim("bikes:repairs", 0, 2);
}).thenCompose(res21 -> {
System.out.println(res21); // >>> OK
return asyncCommands.lrange("bikes:repairs", 0, -1);
})
.thenAccept(System.out::println)
// >>> [bike:5, bike:4, bike:3]
.toCompletableFuture();Java(同步 - Jedis)
long res27 = jedis.rpush("bikes:repairs", "bike:1", "bike:2", "bike:3", "bike:4", "bike:5");
System.out.println(res27); // >>> 5
String res28 = jedis.ltrim("bikes:repairs", 0, 2);
System.out.println(res28); // >>> OK
List<String> res29 = jedis.lrange("bikes:repairs", 0, -1);
System.out.println(res29); // >>> [bike:1, bike:2, bike:3]上述 LTRIM 命令告诉 Redis 仅保留列表从索引 0 到 2 的元素,其余全部丢弃。这形成了一个非常简单但有用的模式:将列表推入操作与列表修剪操作结合,以添加新元素并丢弃超出限制的元素。使用负索引的 LTRIM 可以仅保留最近添加的 3 个元素:
负索引定长列表:使用负索引的 LTRIM 保留最新元素
命令: DEL, RPUSH, LTRIM, LRANGE
Java(异步 - Lettuce)
CompletableFuture<Void> ltrimendoflist = asyncCommands
.rpush("bikes:repairs", "bike:1", "bike:2", "bike:3", "bike:4", "bike:5").thenCompose(res22 -> {
System.out.println(res22); // >>> 5
return asyncCommands.ltrim("bikes:repairs", -3, -1);
}).thenCompose(res23 -> {
System.out.println(res23); // >>> OK
return asyncCommands.lrange("bikes:repairs", 0, -1);
})
.thenAccept(System.out::println)
// >>> [bike:3, bike:4, bike:5]
.toCompletableFuture();Java(同步 - Jedis)
jedis.del("bikes:repairs");
res27 = jedis.rpush("bikes:repairs", "bike:1", "bike:2", "bike:3", "bike:4", "bike:5");
System.out.println(res27); // >>> 5
res28 = jedis.ltrim("bikes:repairs", -3, -1);
System.out.println(res28); // >>> OK
res29 = jedis.lrange("bikes:repairs", 0, -1);
System.out.println(res29); // >>> [bike:3, bike:4, bike:5]以上组合添加新元素并仅保留列表中最新的 3 个元素。通过 LRANGE 您可以访问最顶部的项目,而无需记住非常旧的数据。
注意:虽然 LRANGE 在技术上是一个 O(N) 命令,但访问列表头部或尾部的小范围是常数时间操作。
列表的阻塞操作
列表有一个特殊功能,使其适合实现队列,并通常作为进程间通信系统的构建块:阻塞操作。
假设您希望使用一个进程将项目推入列表,并使用另一个进程来实际处理这些项目。这是典型的生产者/消费者设置,可以通过以下简单方式实现:
然而,有时列表为空,没有可处理的内容,因此 RPOP 返回 NULL。在这种情况下,消费者被迫等待一段时间,然后再次尝试 RPOP。这称为 轮询,在此上下文中不是一个好主意,因为它有几个缺点:
- 强制 Redis 和客户端处理无用的命令(列表为空时所有请求都不会完成实际工作,只会返回 NULL)。
- 增加了项目处理的延迟,因为工作线程在收到 NULL 后会等待一段时间。为了减少延迟,我们可以缩短两次
RPOP调用之间的等待时间,但这样会加剧问题 1,即更多无用的 Redis 调用。
因此 Redis 实现了名为 BRPOP 和 BLPOP 的命令,它们是 RPOP 和 LPOP 的阻塞版本:它们会在列表为空时阻塞,直到有新元素添加到列表或用户指定的超时时间到达时才返回给调用者。
以下是在工作线程中使用的 BRPOP 调用示例:
阻塞操作:使用 BRPOP 等待元素并设置超时,实现生产者-消费者模式而无需轮询
命令: DEL, RPUSH, BRPOP
Java(异步 - Lettuce)
CompletableFuture<Void> brpop = asyncCommands.rpush("bikes:repairs", "bike:1", "bike:2").thenCompose(res24 -> {
System.out.println(res24); // >>> 2
return asyncCommands.brpop(1, "bikes:repairs");
}).thenCompose(res25 -> {
System.out.println(res25);
// >>> KeyValue[bikes:repairs, bike:2]
return asyncCommands.brpop(1, "bikes:repairs");
}).thenCompose(res26 -> {
System.out.println(res26);
// >>> KeyValue[bikes:repairs, bike:1]
return asyncCommands.brpop(1, "bikes:repairs");
})
.thenAccept(System.out::println) // >>> null
.toCompletableFuture();Java(同步 - Jedis)
jedis.del("bikes:repairs");
long res31 = jedis.rpush("bikes:repairs", "bike:1", "bike:2");
System.out.println(res31); // >>> 2
List<String> res32 = jedis.brpop(1, "bikes:repairs");
System.out.println(res32); // >>> (bikes:repairs, bike:2)
List<String> res33 = jedis.brpop(1,"bikes:repairs");
System.out.println(res33); // >>> (bikes:repairs, bike:1)
List<String> res34 = jedis.brpop(1,"bikes:repairs");
System.out.println(res34); // >>> null它的意思是:“等待列表 bikes:repairs 中的元素,但如果 1 秒后没有元素可用则返回”。
注意,您可以使用 0 作为超时时间来永远等待元素,并且您可以指定多个列表而不仅一个,以同时等待多个列表,并在第一个列表收到元素时得到通知。
关于 BRPOP 的几点说明:
- 客户端按顺序被服务:第一个阻塞等待列表的客户端,当其他客户端推入元素时最先被服务,依此类推。
- 返回值与
RPOP不同:它是一个双元素数组,因为BRPOP和BLPOP能够阻塞等待来自多个列表的元素。 - 如果达到超时,返回 NULL。
关于列表和阻塞操作还有更多内容。我们建议您阅读以下内容:
键的自动创建与删除
到目前为止,在示例中我们从不需要在推送元素之前创建空列表,也不需要在列表为空时删除它们。当列表为空时,Redis 负责删除键;或者如果键不存在并且我们尝试向其中添加元素(例如使用 LPUSH),Redis 会创建一个空列表。
这不仅适用于列表,也适用于所有由多个元素组成的 Redis 数据类型——流、集合、有序集合和哈希。
基本上,我们可以总结为三个规则:
- 当我们向聚合数据类型添加元素时,如果目标键不存在,则在添加元素之前会创建一个空的聚合数据类型。
- 当我们从聚合数据类型中移除元素时,如果值保持为空,则键会被自动销毁。流数据类型是此规则的唯一例外。
- 对空键调用只读命令(如
LLEN(返回列表长度))或移除元素的写命令,总是产生与键持有该命令期望类型的空聚合数据类型相同的结果。
规则 1 示例:
自动创建:向不存在的键推送时,Redis 自动创建列表,简化初始化
命令: DEL, LPUSH
Java(异步 - Lettuce)
CompletableFuture<Void> rule1 = asyncCommands.del("new_bikes").thenCompose(res27 -> {
System.out.println(res27); // >>> 0
return asyncCommands.lpush("new_bikes", "bike:1", "bike:2", "bike:3");
})
.thenAccept(System.out::println) // >>> 3
.toCompletableFuture();Java(同步 - Jedis)
jedis.del("new_bikes");
long res36 = jedis.lpush("new_bikes", "bike:1", "bike:2", "bike:3");
System.out.println(res36); // >>> 3但是,如果键存在,我们不能对错误类型执行操作:
类型安全:Redis 阻止错误类型的操作,对非列表键使用列表命令时返回 WRONGTYPE
命令: DEL, SET, TYPE, LPUSH
Java(异步 - Lettuce)
CompletableFuture<Void> rule11 = asyncCommands.set("new_bikes_string", "bike:1").thenCompose(res28 -> {
System.out.println(res28); // >>> OK
return asyncCommands.type("new_bikes_string");
}).thenCompose(res29 -> {
System.out.println(res29); // >>> string
return asyncCommands.lpush("new_bikes_string", "bike:2", "bike:3");
}).handle((res, ex) -> {
if (ex == null) {
return res;
} else {
System.out.println(ex);
// >>> java.util.concurrent.CompletionException:
// >>> io.lettuce.core.RedisCommandExecutionException:
// >>> WRONGTYPE Operation against a key holding the wrong
// >>> kind of value
return -1L;
}
})
.thenAccept(System.out::println) // >>> -1
.toCompletableFuture();Java(同步 - Jedis)
jedis.del("new_bikes");
String res37 = jedis.set("new_bikes", "bike:1");
System.out.println(res37); // >>> OK
String res38 = jedis.type("new_bikes");
System.out.println(res38); // >>> string
try {
long res39 = jedis.lpush("new_bikes", "bike:2", "bike:3");
} catch (Exception e) {
e.printStackTrace();
// >>> redis.clients.jedis.exceptions.JedisDataException:
// >>> WRONGTYPE Operation against a key holding the wrong kind of value
}规则 2 示例:
自动删除:列表为空时,Redis 自动删除键,释放内存
命令: DEL, LPUSH, EXISTS, LPOP
Java(异步 - Lettuce)
CompletableFuture<Void> rule2 = asyncCommands.lpush("bikes:repairs", "bike:1", "bike:2", "bike:3")
.thenCompose(res30 -> {
System.out.println(res30); // >>> 3
return asyncCommands.exists("bikes:repairs");
}).thenCompose(res31 -> {
System.out.println(res31); // >>> 1
return asyncCommands.lpop("bikes:repairs");
}).thenCompose(res32 -> {
System.out.println(res32); // >>> bike:3
return asyncCommands.lpop("bikes:repairs");
}).thenCompose(res33 -> {
System.out.println(res33); // >>> bike:2
return asyncCommands.lpop("bikes:repairs");
}).thenCompose(res34 -> {
System.out.println(res34); // >>> bike:1
return asyncCommands.exists("bikes:repairs");
})
.thenAccept(System.out::println) // >>> 0
.toCompletableFuture();Java(同步 - Jedis)
jedis.del("bikes:repairs");
jedis.lpush("bikes:repairs", "bike:1", "bike:2", "bike:3");
System.out.println(res36); // >>> 3
boolean res40 = jedis.exists("bikes:repairs");
System.out.println(res40); // >>> true
String res41 = jedis.lpop("bikes:repairs");
System.out.println(res41); // >>> bike:3
String res42 = jedis.lpop("bikes:repairs");
System.out.println(res42); // >>> bike:2
String res43 = jedis.lpop("bikes:repairs");
System.out.println(res43); // >>> bike:1
boolean res44 = jedis.exists("bikes:repairs");
System.out.println(res44); // >>> false所有元素弹出后,键不再存在。
规则 3 示例:
Nil 处理:对不存在键的只读命令返回空结果(0 或 nil),将其视为空列表
命令: DEL, LLEN, LPOP
Java(异步 - Lettuce)
CompletableFuture<Void> rule3 = asyncCommands.del("bikes:repairs").thenCompose(res35 -> {
System.out.println(res35); // >>> 0
return asyncCommands.llen("bikes:repairs");
}).thenCompose(res36 -> {
System.out.println(res36); // >>> 0
return asyncCommands.lpop("bikes:repairs");
})
.thenAccept(System.out::println) // >>> null
.toCompletableFuture();Java(同步 - Jedis)
jedis.del("bikes:repairs");
long res46 = jedis.llen("bikes:repairs");
System.out.println(res46); // >>> 0
String res47 = jedis.lpop("bikes:repairs");
System.out.println(res47); // >>> null限制
Redis 列表的最大长度为 2^32 - 1(4,294,967,295)个元素。
性能
访问列表头部或尾部的操作是 O(1),非常高效。但操作列表内部元素的命令通常是 O(n)。示例包括 LINDEX、LINSERT 和 LSET。在大型列表上运行这些命令时请谨慎。
替代方案
当需要存储和处理不确定序列的事件时,可考虑使用 Redis 流 作为列表的替代方案。
了解更多
- Redis Lists Explained 是一个关于 Redis 列表的简短、全面的视频讲解。
- Redis University 的 RU101 详细介绍了 Redis 列表。