列表List
List类型用来存储多个有序的字符串,列表中每个字符串成为元素(element);
一个List最多可以存储2^32-1个元素;
可以对List两端插入(push)或弹出(pop);
List可以充当栈和队列的角色
List的特点:
- List中的Element是有序的,可以通过索引下标获取某个Element或某个范围内的Element List
- List中的Element是可以重复的
常用命令
添加
从右边插入元素:rpush key value [value …]
从左边插入元素:lpush key value [value …]
向某个元素前或后插入元素:linsert key before|after pivot value (pivot为指定的某个元素 )
从左到右列出列表的所有元素:lrange listkey 0 -l
查找
获取指定范围的元素列表:lrange key start end,索引下标从左到右为从0到N-1,从右到左是-1到-N,end包含了自身
获取列表指定索引下标的元素: lindex key index
获取列表长度:llen key
删除
从列表左侧弹出元素:lpop key
从列表右侧弹出元素:rpop key
删除指定元素:lrem key count value,从列表中找到值为value的元素删除,根据count不同分为三种情况:
count>0:从左到右,删除最多count个元素
count<0:从右到左,删除最多count绝对值个元素
count=0:删除所有
修改
修改指定索引下标的元素:lset key index newValue
阻塞
blpop key [key …] timeout
brpop key [key …] timeout
blpop和brpop是lpop和rpop的阻塞版本,除了弹出方向不同,使用方法基本相同。
timeout的单位是秒
- 列表为空:timeout=0,客户端会一直阻塞;timeout=3,会等待3秒返回
- 列表不为空,timeout=0,客户端立即返回
使用brpop时需要注意两点:
- 如果是多个键,brpop会从左到右遍历所有键,一旦有一个键能弹出元素,客户端立即返回
- 如果多个客户端对同一个键执行brpop,最先执行brpop命令的客户端可以获取到弹出的值
列表类型的命令及对应的时间复杂度
命令 |
时间复杂度 |
rpush key value [value …] |
O(k),k是元素个数 |
lpush key value [value….] |
O(k),k是元素个数 |
linsert ket before|after pivot value |
O(n),n是pivot距离列表头或尾的距离 |
lrange key start end |
O(s+n),s是start偏移量,n是start到end的范围 |
lindex key index |
O(n),n是索引的偏移量 |
llen key |
O(1) |
lpop key |
O(1) |
rpop key |
O(1) |
lrem key count value |
O(n),n是列表长度 |
ltrim key start end |
O(n),n是要裁剪的元素总数 |
lset key index newvalue |
O(n),n是索引的偏移量 |
blpop key [key …] timeout |
O(1) |
brpop key [key…] timeout |
O(1) |
内部编码
- ziplist:压缩列表,当列表的元素个数小于list-max-ziplist-entries配置(默认512个),同时列表中每个元素的值都小于list-max-ziplist-value配置(默认64个字节),Redis会选用ziplist作为列表的内部实现来减少内存的使用
- linkedlist:链表,当列表类型无法满足ziplist的条件,Redis会选用linkedlist作为List的内部实现
- quicklist:Redis3.2提供,是一个以ziplist为节点的linkedlist,结合了两者的优势
使用场景:
- 消息队列:lpush+brpop组合可以实现阻塞队列,生产者客户端使用lrpush从列表左侧插入元素,多个消费者客户端使用brpop命令阻塞式“抢”列表右侧的元素,多个客户端保证了消费的负载均衡和高可用
- 文章列表:每个用户都有属于自己的文章列表,需要分页展示文章列表,可以使用List,因为List不但是有序的,还支持按照索引范围获取元素。
(1)、 每篇文章使用Hash结果存储
(2)、向用户文章列表添加文章使用lpush
(3)、分页获取用户文章列表使用lrange
注意:如果每次分页获取文章个数较多,使用hgetall效率较低,可以考虑使用pipeline批量获取,或者将文章数据序列化成字符串,使用mget批量获取。
lrange在列表两端性能较好,中间性能较差,可以将列表做二级拆分,或者使用Redis3.2的quicklist内部编码实现,效率较高。
场景选择时可以参考一下口诀
lpush + lpop = Stack(栈)
lpush + rpop = Queue(队列)
lpush + ltrim = Capped Collection(有限集合)
lpush + brpop = Message Queue(消息队列)