你可能还记得在第 1 章中,LIST 允许你从序列的两端推送和弹出项目,获取单个项目,并执行列表的各种其他预期操作。LIST 本身非常适合保存工作项队列、最近查看的文章或收藏联系人。
在本节中,我们将讨论 LIST,它存储 STRING 值的有序序列。我们将介绍一些最常用的 LIST 操作命令,用于从 LIST 推送和弹出项目。阅读本节后,你将知道如何使用最常用的命令来操作 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 章中介绍了其中几个,以及 LINDEX 和 LRANGE。下一个清单显示了这些推送和弹出命令的一些用法。
>>> 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 结合起来,得到一个功能类似于 LPOP 或 RPOP 调用的东西,它可以一次返回和弹出多个项目。我们将在本章的后面讨论如何使这些复合命令具有原子性1,并在第 4 章中更深入地探讨更高级的 Redis 风格的事务。
在我们没有在第 1 章中介绍的 LIST 命令中,有一些命令允许你将项目从一个列表移动到另一个列表,甚至在等待其他客户端向 LIST 添加项目时进行阻塞。表 3.4 显示了我们的阻塞弹出和项目移动命令。
命令 | 示例用法和描述 |
---|---|
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 从多个列表弹出项目的示例。
>>> 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 章中介绍这些内容。
在第 2.1 节和第 2.5 节中,我们使用 ZSET 来保存最近查看项目的列表。这些最近查看的项目包括时间戳作为分数,以便我们在清理期间或购买后执行分析。但是包含这些时间戳会占用空间,如果我们的分析不需要时间戳,那么使用 ZSET 只会浪费空间。尝试用 LIST 替换 update_token() 中 ZSET 的使用,同时保持相同的语义。提示:如果你发现自己卡住了,你可以跳到第 6.1.1 节,以获得正确的方向。
LIST 的主要好处之一是它们可以包含多个字符串值,这可以让你将数据组合在一起。SET 提供了类似的功能,但需要注意的是,给定 SET 中的所有项目都是唯一的。让我们看看这如何改变我们使用 SET 所能做的事情。
1 在 Redis 中,当我们谈论一组命令是原子的时候,我们的意思是,当我们读取或更改相同的数据时,没有其他客户端可以读取或更改数据。