List 列表
命令
LPUSH 将⼀个或者多个元素从左侧放⼊(头插)到 list 中。 语法:LPUSH key element [element ...]
命令有效版本:1.0.0 之后 时间复杂度:只插⼊⼀个元素为 O(1), 插⼊多个元素为 O(N), N 为插⼊元素个数. 返回值:插⼊后 list 的⻓度。
LPUSHX 在 key 存在时,将⼀个或者多个元素从左侧放⼊(头插)到 list 中。不存在,直接返回 语法:LPUSHX key element [element ...]
命令有效版本:2.0.0 之后 时间复杂度:只插⼊⼀个元素为 O(1), 插⼊多个元素为 O(N), N 为插⼊元素个数. 返回值:插⼊后 list 的⻓度。
RPUSH 将⼀个或者多个元素从右侧放⼊(尾插)到 list 中。 语法:RPUSH key element [element ...]
命令有效版本:1.0.0 之后 时间复杂度:只插⼊⼀个元素为 O(1), 插⼊多个元素为 O(N), N 为插⼊元素个数. 返回值:插⼊后 list 的⻓度。
RPUSHX 在 key 存在时,将⼀个或者多个元素从右侧放⼊(尾插)到 list 中。 语法:RPUSHX key element [element ...]
命令有效版本:2.0.0 之后 时间复杂度:只插⼊⼀个元素为 O(1), 插⼊多个元素为 O(N), N 为插⼊元素个数. 返回值:插⼊后 list 的⻓度。
LRANGE 获取从 start 到 end 区间的所有元素,左闭右闭。 语法:LRANGE key start stop
命令有效版本:1.0.0 之后 时间复杂度:O(N) 返回值:指定区间的元素。
LPOP 从 list 左侧取出元素(即头删)。 语法:LPOP key
命令有效版本:1.0.0 之后 时间复杂度:O(1) 返回值:取出的元素或者 nil。
RPOP 从 list 右侧取出元素(即尾删)。 语法:RPOP key
命令有效版本:1.0.0 之后 时间复杂度:O(1) 返回值:取出的元素或者 nil。
LINDEX 获取从左数第 index 位置的元素。 语法:LINDEX key index
命令有效版本:1.0.0 之后 时间复杂度:O(N) 返回值:取出的元素或者 nil。
LINSERT 在特定位置插⼊元素。 语法:LINSERT key <BEFORE | AFTER> pivot element
命令有效版本:2.2.0 之后 时间复杂度:O(N) 返回值:插⼊后的 list ⻓度。
LLEN 获取 list ⻓度。 语法:LLEN key
命令有效版本:1.0.0 之后 时间复杂度:O(1) 返回值:list 的⻓度。
阻塞版本命令
blpop 和 brpop 是 lpop 和 rpop 的阻塞版本,和对应⾮阻塞版本的作⽤基本⼀致,除了:
• 在列表中有元素的情况下,阻塞和⾮阻塞表现是⼀致的。但如果列表中没有元素,⾮阻塞版本会理 解返回 nil,但阻塞版本会根据 timeout,阻塞⼀段时间,期间 Redis 可以执⾏其他命令,但要求执 ⾏该命令的客⼾端会表现为阻塞状态
• 命令中如果设置了多个键,那么会从左向右进⾏遍历键,⼀旦有⼀个键对应的列表中可以弹出元 素,命令⽴即返回。
• 如果多个客⼾端同时多⼀个键执⾏ pop,则最先执⾏命令的客⼾端会得到弹出的元素。
BLPOP LPOP 的阻塞版本。 语法:BLPOP key [key ...] timeout
命令有效版本:1.0.0 之后 时间复杂度:O(1) 返回值:取出的元素或者 nil。
BRPOP RPOP 的阻塞版本。 语法:BRPOP key [key ...] timeout
命令有效版本:1.0.0 之后 时间复杂度:O(1) 返回值:取出的元素或者 nil。
内部编码
列表类型的内部编码有两种:
• ziplist(压缩列表):当列表的元素个数⼩于 list-max-ziplist-entries 配置(默认 512 个),同时 列表中每个元素的⻓度都⼩于 list-max-ziplist-value 配置(默认 64 字节)时,Redis 会选⽤ ziplist 来作为列表的内部编码实现来减少内存消耗。
• linkedlist(链表):当列表类型⽆法满⾜ ziplist 的条件时,Redis 会使⽤ linkedlist 作为列表的内 部实现。
使⽤场景
消息队列 Redis 可以使⽤ lpush + brpop 命令组合实现经典的阻塞式⽣产者-消费者模型队列,⽣产者客⼾端使⽤ lpush 从列表左侧插⼊元素,多个消费者客⼾端使⽤ brpop 命令阻塞式地从队列中"争抢" 队⾸元素。通过多个客⼾端来保证消费的负载均衡和⾼可⽤性。
微博 Timeline 每个⽤⼾都有属于⾃⼰的 Timeline(微博列表),现需要分⻚展⽰⽂章列表。此时可以考虑使⽤ 列表,因为列表不但是有序的,同时⽀持按照索引范围获取元素。
此⽅案在实际中可能存在两个问题:
-
1 + n 问题。即如果每次分⻚获取的微博个数较多,需要执⾏多次 hgetall 操作,此时可以考虑使⽤ pipeline(流⽔线)模式批量提交命令,或者微博不采⽤哈希类型,⽽是使⽤序列化的字符串类 型,使⽤ mget 获取。
-
分裂获取⽂章时,lrange 在列表两端表现较好,获取列表中间的元素表现较差,此时可以考虑将列 表做拆分。
选择列表类型时,请参考:
同侧存取(lpush + lpop 或者 rpush + rpop)为栈
异侧存取(lpush + rpop 或者 rpush + lpop)为队列