1.List列表
列表类型适用于存储多个有序的字符串(这里的有序指的是强调数据排列顺序的重要,不是升序降序的意思),列表中的每个字符串称为元素(element),一个列表最多可以存储2^32-1个元素。在Redis中,可以对列表两端插入(push)和弹出(pop),还可以获取指定范围的元素列表,获取指定的索引下标的元素等。列表事宜总比较灵活的数据结构,它可以充当栈和队列的角色,在实际开发商有很多应用场景
1.1 List列表允许两端进行插入和删除操作
1.2列表类型的特点
1.列表中元素是有序的,这意味着可以通过 索引下标获取某个元素或者某个范围的元素列表
2.区分获取和删除的区别
3.列表中的元素是允许重复的
2.常见命令
2.1 LPUSH
将一个或者多个元素从左侧放入(头插)到list中
语法:LPUSH key element [element ... ]
时间复杂度:只插入一个元素为O(1),插入多个元素为O(N),N为插入元素的个数
返回值:插入后list的长度
示例:
2.2 LPUSHX
在Key存在时,将一个或者多个元素从左侧插入(头插)到list中。不存在,直接返回0
语法:LPUSHX key element [element ...]
时间复杂度:只插入一个元素为O(1),插入多个元素为O(N),N为插入元素个数
返回值:插入后list的长度
示例:
2.3 RPUSH
将一个或者多个元素从右侧插入(尾插)到list中
语法:RPUSH key element [element ...]
时间复杂度:直插入一个元素为O(1),插入多个元素为O(N),N为插入元素个数
返回值:插入后list的长度
示例:
2.4 RPUSHX
在Key存在时,将一个或者多个元素从右侧插入(尾插)到list中
语法:RPUSHX key element [element ...]
时间复杂度:只插入一个元素为O(1),插入多个元素为O(N),N为插入元素个数
返回值:插入后list的长度
示例:
2.5 LRANGE
获取start到end区间的所有元素,左闭右闭
语法:LRANGE key start stop
时间复杂度:O(N)
返回值:指定区间的元素
示例:
当end大于list中的元素个数时, list会“尽力展示”
2.6 LPOP
从list左侧取出元素(即头删)
语法:LPOP key
时间复杂度:O(1)
返回值:取出元素或者nil
示例:
2.7 RPOP
从list右侧取出元素(即尾删)
语法:RPOP key [count](从Redis6.2版本新加,count指要删除的元素个数)
时间复杂度:O(1)
返回值:取出的元素或者nil
Redis中的list是一个双向链表
搭配使用rpush和lpop,就相当于队列
搭配使用rpush和rpop,就相当于栈
2.8 LINDEX
获取从左数第INDEX位置的元素
语法:LINDEX key index
时间复杂度:O(N),N指list中元素的个数
返回值:取出的元素或者nil(下标非法时,返回nil)
示例:
2.9 LINSERT
在特定位置插入元素
语法:LINSERT key <BEFORE | AFTER> pivot element(pivot指的是元素,不是索引下标)
时间复杂度:O(1)
返回值:插入后的list长度,当pivot不存在时,返回-1
示例:
2.10 LLEN
获取list长度
语法:LLEN key
时间复杂度:O(1)
返回值:list的长度
示例:
2.11 LTRIM
保留[start,stop]区间的元素,区间之外的元素全部删除
语法:LTRIM key start end
时间复杂度:O(N)
返回值:保留正确返回OK
2.12 LREM
从list中删除某制定元素制定次
语法:LREM key count element(count表示要删除的个数,element要删除的值)
时间复杂度:O(N),N是列表中元素的个数
注意:count>0,从左向右删除等于element的值count次
count<0,从右向左删除等于element的值count次
count=0,删除所有等于element的元素
返回值:删除元素的个数
2.13 LSET
将下标为index的元素设置为element,index越界会报错
语法:LSET key index element
时间复杂度:O(N)
返回值:设置成功返回OK,index越界会报错
阻塞版本命令
blpop和brpop是lpop和rpop的阻塞版本,和对应非阻塞版本的作用基本一致,除了一下几点:
1.在列表中有元素的情况下,阻塞和非阻塞表现是一致的。但如果列表中没有元素,非阻塞版本会理解返回nil,但阻塞版本会根据timeout,阻塞一段时间(并不会对Redis服务器产生负面影响),期间Redis可以执行其他命令,但要求执行该命令的客户端会表现为阻塞状态
2.命令中如果设置了多个键,那么会从左向右进行遍历键,一旦有一个件对应的列表中可以弹出元素,命令立即返回
3.如果多个客户端同时对一个键执行pop,则最先执行命令的客户端会得到弹出的元素(单线程模型,不会出现线程安全问题)
2.14 BLPOP
LPOP的阻塞版本
语法:BLPOP key [key ...] timeout(timeout超时时间,Redis6可以使用小数)
时间复杂度:O(1)
注意:每个key对应一个list,如果这些list有任何一个非空,blpop都能够把这里的元素给获取到,立即返回,如果这些list都为空,此时就需要阻塞等待,等待其他客户端往这些list中插入元素
返回值:取出元素或者nil
示例:
2.15 BRPOP
RPOP的阻塞版本
语法:BRPOP key [key ...] timeout
时间复杂度:O(1)
返回值:取出的元素或者nil
示例:
3.List的内部编码
3.1 ziplist
ziplist(压缩列表):当列表中的元素小于list-max-ziplist-entries配置(默认为512个),同时列表中的每个元素的长度都小于list-max-ziplist-value配置(默认64字节)时,Redis会选择用ziplist来作为列表的内部编码实现来减少内存消耗
3.2 linkedlist
linkedlist(链表):当列表类型无法满足ziplist的条件时,Redis会使用linkedlist作为列表的内部实现
4.List的典型使用场景
4.1 Redis阻塞消息队列模型
Redis可以使用lpush和brpop命令组合实现经典的阻塞式生产者-消费者模型队列,生产者客户端使用lpush从列表左侧插入元素,多个消费者客户端使用brpop命令阻塞式地从队列中“争抢”队首元素。通过多个客户端来保证消费的负载均衡和高可用性
Redis分频道的消息队列
Redis同样使用lpush+brpop命令,但通过不同的键模拟频道的概念,不同的消费者可以通过brpop不同键值,实现订阅不同频道的理念
多个列表/频道,这种场景非常常见的。日常使用的一些程序如抖音等,可以使用一个通道,来传输视频,还可以使用一个通道,来传输弹幕,还可以有一个通道(频道),来传输点赞,转发,收藏数据,还可以有频道来传输评论,数据......多个频道,就可以在某种数据发生问题的时候不会对其他数据造成影响(解耦合)
4.2 微博Timeline
每个用户都有属于自己的Timeline(微博列表),现需要分页展示文章列表。此时可以考虑使用列表,因为列表不但是有序的,同时支持按照索引范围获取元素
伪代码:
keylist = lrange user:1:myblogs 0 9
for key in keylist{hgetall key
}
此方案仍存在问题:
1.1+n问题。即如果每次分页获取的微博个数较多,需要执行多次hgetall操作,此时可以考虑使用pipline(流水线)模式批量提交命令,或者微博不采用哈希类型,而是使用序列化的字符串类型,使用mget获取
2.分裂获取文章时,lrange在列表两端的表现较好,但是获取列表中间的元素表现较差(时间复杂度为O(N)),此时可以考虑将列表拆分
4.3栈
同侧存取(lpush+lpop或者rpush+rpop)为栈
4.4队列
异侧存取(lpush+rpop或者rpush+lpop)为队列