dot Redis 8 来了——而且是开源的

了解更多

3.2 列表

返回首页

3.2 列表

你可能还记得在第 1 章中,LIST 允许你从序列的两端推送和弹出项目,获取单个项目,并执行列表的各种其他预期操作。LIST 本身非常适合保存工作项队列、最近查看的文章或收藏联系人。

在本节中,我们将讨论 LIST,它存储 STRING 值的有序序列。我们将介绍一些最常用的 LIST 操作命令,用于从 LIST 推送和弹出项目。阅读本节后,你将知道如何使用最常用的命令来操作 LIST。我们将从查看表 3.3 开始,你可以在其中看到一些最常用的 LIST 命令。

表 3.3 一些常用的 LIST 命令
命令 示例用法和描述
RPUSH RPUSH key-name value [value …] — 将值推送到列表的右端
LPUSH LPUSH key-name value [value …] — 将值推送到列表的左端
RPOP RPOP key-name — 删除并返回列表最右边的项目
LPOP LPOP key-name — 删除并返回列表最左边的项目
LINDEX LINDEX key-name offset — 返回给定偏移量的项目
LRANGE LRANGE key-name start end — 返回列表中从开始到结束(包括)的偏移量的项目
LTRIM LTRIM key-name start end — 修剪列表,使其仅包含开始和结束之间(包括)索引处的项目

LIST 推送命令的语义不应令人惊讶,弹出命令也不应令人惊讶。我们在第 1 章中介绍了其中几个,以及 LINDEXLRANGE。下一个清单显示了这些推送和弹出命令的一些用法。

清单 3.3 一个示例交互,显示 Redis 中的 LIST 推送和弹出命令
>>> conn.rpush('list-key', 'last')
1L

当我们把项目推到列表上时,它会返回推完成后列表的长度。

>>> conn.lpush('list-key', 'first')

我们可以很容易地在列表的两端进行推送。

2L
>>> conn.rpush('list-key', 'new last')
3L
>>> conn.lrange('list-key', 0, -1)
['first', 'last', 'new last']

从语义上讲,列表的左端是开头,列表的右端是结尾。

>>> conn.lpop('list-key')
'first'
>>> conn.lpop('list-key')
'last'

重复地从左边弹出项目将从左到右返回项目。

>>> conn.lrange('list-key', 0, -1)
['new last']
>>> conn.rpush('list-key', 'a', 'b', 'c')

我们可以同时推送多个项目。

4L
>>> conn.lrange('list-key', 0, -1)
['new last', 'a', 'b', 'c']
>>> conn.ltrim('list-key', 2, -1)
True
>>> conn.lrange('list-key', 0, -1)
['b', 'c']

我们可以从开头、结尾或两端修剪任意数量的项目。

LTRIM 命令在本例中是新的,我们可以将它与 LRANGE 结合起来,得到一个功能类似于 LPOPRPOP 调用的东西,它可以一次返回和弹出多个项目。我们将在本章的后面讨论如何使这些复合命令具有原子性1,并在第 4 章中更深入地探讨更高级的 Redis 风格的事务。

在我们没有在第 1 章中介绍的 LIST 命令中,有一些命令允许你将项目从一个列表移动到另一个列表,甚至在等待其他客户端向 LIST 添加项目时进行阻塞。表 3.4 显示了我们的阻塞弹出和项目移动命令。

表 3.4 一些用于阻塞 LIST 弹出和在 LIST 之间移动项目的 LIST 命令
命令 示例用法和描述
BLPOP BLPOP key-name [key-name …] timeout — 从第一个非空 LIST 中弹出最左边的项目,或者等待超时(以秒为单位)以获取项目
BRPOP BRPOP key-name [key-name …] timeout — 从第一个非空 LIST 中弹出最右边的项目,或者等待超时(以秒为单位)以获取项目
RPOPLPUSH RPOPLPUSH source-key dest-key — 从源中弹出最右边的项目,并将该项目 LPUSH 到目标,同时将该项目返回给用户
BRPOPLPUSH BRPOPLPUSH source-key dest-key timeout — 从源中弹出最右边的项目,并将该项目 LPUSH 到目标,同时将该项目返回给用户,如果源为空,则最多等待超时时间

当我们讨论第 6 章中的队列时,这组命令特别有用。下面的清单显示了一些使用 BRPOPLPUSH 移动项目和使用 BLPOP 从多个列表弹出项目的示例。

清单 3.4 Redis 中阻塞 LIST 弹出和移动命令
>>> conn.rpush('list', 'item1')
1
>>> conn.rpush('list', 'item2')
2
>>> conn.rpush('list2', 'item3')
1

让我们先向几个列表中添加一些项目。

>>> conn.brpoplpush('list2', 'list', 1)
'item3'

让我们将一个项目从一个列表移动到另一个列表,同时返回该项目。

>>> conn.brpoplpush('list2', 'list', 1)

当列表为空时,阻塞弹出将停滞一段时间,并返回 None(在交互式控制台中不显示)。

>>> conn.lrange('list', 0, -1)
['item3', 'item1', 'item2']

我们从“list2”中弹出了最右边的项目,并将其推到“list”的左边。

>>> conn.brpoplpush('list', 'list2', 1)
'item2'
>>> conn.blpop(['list', 'list2'], 1)
('list', 'item3')
>>> conn.blpop(['list', 'list2'], 1)
('list', 'item1')
>>> conn.blpop(['list', 'list2'], 1)
('list2', 'item2')
>>> conn.blpop(['list', 'list2'], 1)

从这些列表中阻塞左弹出项目将按传递的顺序检查列表中的项目,直到它们为空。

>>>

使用阻塞弹出命令以及弹出/推送组合命令的最常见用例是在消息传递和任务队列的开发中,我们将在第 6 章中介绍这些内容。

练习:使用 LIST 减少内存使用

在第 2.1 节和第 2.5 节中,我们使用 ZSET 来保存最近查看项目的列表。这些最近查看的项目包括时间戳作为分数,以便我们在清理期间或购买后执行分析。但是包含这些时间戳会占用空间,如果我们的分析不需要时间戳,那么使用 ZSET 只会浪费空间。尝试用 LIST 替换 update_token()ZSET 的使用,同时保持相同的语义。提示:如果你发现自己卡住了,你可以跳到第 6.1.1 节,以获得正确的方向。

LIST 的主要好处之一是它们可以包含多个字符串值,这可以让你将数据组合在一起。SET 提供了类似的功能,但需要注意的是,给定 SET 中的所有项目都是唯一的。让我们看看这如何改变我们使用 SET 所能做的事情。

1 在 Redis 中,当我们谈论一组命令是原子的时候,我们的意思是,当我们读取或更改相同的数据时,没有其他客户端可以读取或更改数据。