Redis的list类型

什么是list

列表类型是用来存储多个有序的字符串,redis的list类似于”双端队列“,可以对列表两端进行插入(push)和弹出(pop)。如下图所示:
Redis的list类型_第1张图片

注意:

  1. 这里的有序指的是”顺序很关键“,如果把元素位置颠倒,顺序调换,此时得到新的list和之前的list是不等价的
  2. 列表中的元素是允许重复的,而像hash类型的field是不能重复的

常用命令

lpush

将一个或多个元素从左侧添加到list中,即头插
语法:lpush key element [element...]
时间复杂度:O(1)
返回值:插入后list的长度
image.png

lpushx

在key存在时,将一个或多个元素左侧添加到list中。不存在,直接返回
语法:lpushx key element [element...]
时间复杂度:O(1)
返回值:插入后list的长度
Redis的list类型_第2张图片

rpush/rpushx

用法和lpush/lpushx一致,唯一区别:将一个或多个元素添加到list中,即尾插

lrange

获取从start到stop区间的所有元素,闭区间
语法:lrange key start stop
时间复杂度:O(n)
返回值:返回指定区间的元素
Redis的list类型_第3张图片
lrange给定下标越界问题
在redis中,是尽可能的获取到给定区间的区间,如果给定区间越界,会尽可能获取对应的内容。如下所示:
Redis的list类型_第4张图片
我们把上述这样的特性称为”鲁棒性“:你对我越粗鲁,我就表现的越棒。提高了程序的容错率

lpop

从list左侧删除一个元素(头删)
语法:lpop key
时间复杂度:O(1)
返回值:删除的元素或nil
image.png

rpop

从list右侧删除一个元素(尾删)
语法:rpop key
时间复杂度:O(1)
返回值:删除的元素或nil
image.png

搭配使用rpush和lpop,就是一个队列
搭配使用rpush和rpop,就是一个栈

lindex

获取从左到右第index位置的元素
语法:lindex key index
时间复杂度:O(1)
返回值:得到的元素或nil
Redis的list类型_第5张图片

llen

获取list长度
语法:llen key
时间复杂度:O(1)
返回值:list的长度
image.png

linsert

指定位置插入元素
语法:linsert key pivot element
Redis的list类型_第6张图片
当list中存在重复元素时
Redis的list类型_第7张图片

阻塞版本命令

blpopbrooplpoprpop的阻塞版本,如果list中存在元素,blpop/brooplpop/rpop作用相同;如果list为空,blpop/brpop会产生阻塞,一直阻塞到队列不为空。
java的阻塞队列(blockingQueue),在多线程的时候常用于生产者消费者模型中。使用阻塞队列作为中间的”交易场所“,阻塞队列在其中具有两个特性:

  1. 线程安全
  2. 阻塞
    1. 如果队列为空,尝试出队列,会产生阻塞直到队列不为空,阻塞解除
    2. 如果队列满了,尝试入队列,会产生阻塞直到队列不满,阻塞解除

redis的list也可以作为阻塞队列,线程安全是靠单线程支持的,阻塞,则只支持”队列为空“的情况,不考虑”队列满“。
语法:blpop key [key...] timeoutbrpop key [key...] timeout

  • timeout : 使用blpop和brpop的时候,可以显示的设置阻塞时间。在阻塞期间redis可以执行其他命令,所以不会对redis服务器产生负面影响
  • 命令中如果设置多个键(key),那么会从左到右遍历键,多个key对应多个list,多个list哪个有元素,就会返回哪个元素

开两个窗口,两个客服端模拟实现,一个客服端添加元素,一个客服端等待弹出元素

  • 如果多个客服端同时对一个键执行blpop,则最先执行命令的客服端先弹出元素

Redis的list类型_第8张图片

list的内部编码

list底层是一个quicklist, quicklist相当于链表(linkedlist)+ 压缩列表(ziplist)的结合。整体是一个链表,链表上的每个节点是一个压缩列表(把数据按照更紧凑的压缩方式进行表示,节省空间,当元素多的时候,操作元素的效率降低)。

list应用场景

使用redis作为消息队列

redis阻塞消息队列模型

Redis的list类型_第9张图片

redis分频道阻塞消息队列模型

Redis的list类型_第10张图片
使用多个列表/频道(channel)的场景非常常见,例如抖音:一个通道,用来传短视频数据;一个通道,用来传弹幕;一个频道,用来传点赞,转发,收藏数据;一个频道,用来传评论数据。搞多个频道,就可以在某个通道出现问题,不会对其他通道造成影响,解耦合

微博列表

微博列表,需要分页展示文章列表,可以考虑使用list,因为list是有序的,支持索引范围获取元素

  1. 每篇微博使用hash结构存储,有三个属性:title, timestamp, content
    :::tips
    hmset mblog:1 title xx timestamp xxx content xxx

    hmset mblog:n title xx timestamp xxxx content xxx
    :::

  2. 向用户的微博列表添加微博,user::myblogs作为微博的键
    :::tips
    lpush user:1:myblogs myblog:1 myblog:2

    lpush user:k:myblogs myblog:1111
    :::

  3. 分页获取用户的微博列表
    :::tips
    keylist = lrange user:1:myblogs 0 9 获取用户前10篇微博
    for key in keylist {
    hgetall key
    }
    :::
    上诉方案存在两个问题

  4. 在分页获取数据时,如果每次分页获取的微博个数较多时,需要执行多次hgetall,即多次网络请求,可以使用pipeline(流水线)把这些命令合并成一个网络请求进行通信,降低客服端和服务器交互次数。当然也可以让微博不采用hash类型,使用序列化的字符串类型,使用mget获取

  5. 由于lrange在获取列表两端的表现较好,而获取中间元素表现较差,可以考虑多做分页拆分

你可能感兴趣的:(redis,redis,list,数据库)