列表类型来存储多个有序的字符串,a、b、c、d、e 五个元素从左到右组成了一个有序的列表,列表中的每个字符串称为元素,一个列表最多可以存储个元素。在 Redis 中,可以对列表两端插入(push)和弹出(pop),还可以获取指定范围的元素列表、 获取指定索引下标的元素等。列表是一种比较灵活的数据结构,它可以充当栈和队列的角色,在实际开发上有很多应用场景。
列表类型的特点:
第一、列表中的元素是有序的,这意味着可以通过索引下标获取某个元素或者某个范围的元素列表
第二、区分获取和删除的区别,例如lpop会删除元素,而lindex只是获取元素,不会删除
第三、列表中的元素是允许重复的
基本命令
lpush命令
将一个或者多个元素从左侧放入(头插)到 list 中
返回值:插入后 list 的长度
lpush key element [element ...]
lpushx命令
在 key 存在时,将一个或者多个元素从左侧放入(头插)到 list 中。不存在,直接返回
返回值:插⼊后 list 的长度
lpushx key element [element ...]
(一开始,没有key存在,然后用lpush头插到111和222进入key里,key不存在就新建一个。
如果用lpushx,如果key不存在,就不插入,也就是说,lpushx只把数据插入到已经存在的key中)
rpush命令
将一个或者多个元素从右侧放入(尾插)到 list 中
返回值:插⼊后 list 的长度
rpush key element [element ...]
rpushx命令
在 key 存在时,将一个或者多个元素从右侧放入(尾插)到 list 中。
返回值:插⼊后 list 的长度
rpushx key element [element ...]
和上面lpush和lpushx的命令一样,但是是从不同的方向插入
lrange命令
获取从 start 到 end 区间的所有元素,左闭右闭。
返回值:指定区间的元素
lrange key start stop
(可以看到,lpush和rpush按相同顺序插入的结果是不同的)
lpop命令
从 list 左侧取出元素(即头删)
返回值:取出的元素或者 nil。
lpop key
rpop命令
从 list 右侧取出元素(即尾删)
返回值:取出的元素或者 nil。
rpop key
lindex命令
获取从左数第 index 位置的元素
返回值:取出的元素或者 nil
linsert命令
在特定位置插入元素
返回值:插入后的 list 长度
linsert key <before | after> pivot element
(在3前插入555,在1后插入777)
llen命令
获取 list 长度
返回值:list 的长度
阻塞版本命令
blpop命令
lpop 的阻塞版本
返回值:取出的元素或者 nil
blpop key [key ...] timeout
brpop命令
rpop的阻塞版本
返回值:取出的元素或者 nil
brpop key [key ...] timeout
阻塞等待100秒key1和key2的数据到来
开另一个客户端,向key1头插一个数据11111
原本的客户端接收到数据,返回数据(此时数据也被删除了)
内部编码
列表类型的内部编码有两种:
ziplist(压缩列表):当列表的元素个数⼩于 list-max-ziplist-entries 配置(默认 512 个),同时 列表中每个元素的长度都小于 list-max-ziplist-value 配置(默认 64 字节)时,Redis 会选用 ziplist 来作为列表的内部编码实现来减少内存消耗。
linkedlist(链表):当列表类型无法满足 ziplist 的条件时,Redis 会使用 linkedlist 作为列表的内 部实现。
在现在使用的Redis5版本中,已经使用quicklist代替ziplist和linklist
应用场景
消息队列
Redis 可以使用 lpush + brpop 命令组合实现经典的阻塞式生产者-消费者模型队列, 生产者客户端使用 lpush 从列表左侧插入元素,多个消费者客户端使用 brpop 命令阻塞式地从队列中 "争抢" 队首元素。通过多个客户端来保证消费的负载均衡和高可用性。
微博 Timeline
每个用户都有属于自己的 Timeline(微博列表),现需要分页展示文章列表。此时可以考虑使用列表,因为列表不但是有序的,同时支持按照索引范围获取元素。
1)每篇微博使用哈希结构存储,例如微博中 3 个属性:title、timestamp、content:
2)向用户 Timeline 添加微博,user::mblogs 作为微博的键:
3)分页获取用户的 Timeline,例如获取用户 1 的前 10 篇微博:
此方案在实际中可能存在两个问题:
1 + n 问题。即如果每次分页获取的微博个数较多,需要执行多次 hgetall 操作,此时可以考虑使用 pipeline(流水线)模式批量提交命令,或者微博不采用哈希类型,而是使用序列化的字符串类型,使用 mget 获取。
2. 分裂获取文章时,lrange 在列表两端表现较好,获取列表中间的元素表现较差,此时可以考虑将列表做拆分。
选择列表类型时,参考
同侧存取(lpush + lpop 或者 rpush + rpop)为栈
异侧存取(lpush + rpop 或者 rpush + lpop)为队列