• 周六. 7 月 13th, 2024

    Redis数据类型之List

    root

    4 月 27, 2022 #Redis数据类型List

    ist类型是简单字符串列表, 按照插入顺序排序。每个列表最多可以存储2^32-1个元素应用场景:

    1. 消息队列:
    2. 排行榜:list类型的lrange命令可以分页查看队列中的数据。可将每隔一段时间计算一次的排行榜存储在list类型中,如京东每日的手机销量排行、学校每次月考学生的成绩排名、斗鱼年终盛典主播排名等,下图是酷狗音乐“K歌擂台赛”的昨日打擂金曲排行榜,每日计算一次,存储在list类型中,接口访问时,通过page和size分页获取打擂金曲。(打个小广告,酷狗音乐“K歌擂台赛”每天都能产生一批优质翻唱作品,对普通人优质歌声有兴趣的朋友不妨来听听)。

    但是,并不是所有的排行榜都能用list类型实现,只有定时计算的排行榜才适合使用list类型存储,与定时计算的排行榜相对应的是实时计算的排行榜,list类型不能支持实时计算的排行榜,之后在介绍有序集合sorted set的应用场景时会详细介绍实时计算的排行榜的实现。

    3. 最新列表:

    list类型的lpush命令和lrange命令能实现最新列表的功能,每次通过lpush命令往列表里插入新的元素,然后通过lrange命令读取最新的元素列表,如朋友圈的点赞列表、评论列表。

    但是,并不是所有的最新列表都能用list类型实现,因为对于频繁更新的列表,list类型的分页可能导致列表元素重复或漏掉,举个例子,当前列表里由表头到表尾依次有(E,D,C,B,A)五个元素,每页获取3个元素,用户第一次获取到(E,D,C)三个元素,然后表头新增了一个元素F,列表变成了(F,E,D,C,B,A),此时用户取第二页拿到(C,B,A),元素C重复了。只有不需要分页(比如每次都只取列表的前5个元素)或者更新频率低(比如每天凌晨更新一次)的列表才适合用list类型实现。对于需要分页并且会频繁更新的列表,需用使用有序集合sorted set类型实现。另外,需要通过时间范围查找的最新列表,list类型也实现不了,也需要通过有序集合sorted set类型实现,如以成交时间范围作为条件来查询的订单列表。之后在介绍有序集合sorted set类型的应用场景时会详细介绍sorted set类型如何实现最新列表。

    那么问题来了,对于排行榜和最新列表两种应用场景,list类型能做到的sorted set类型都能做到,list类型做不到的sorted set类型也能做到,那为什么还要使用list类型去实现排行榜或最新列表呢,直接用sorted set类型不是更好吗?原因是sorted set类型占用的内存容量是list类型的数倍之多(之后会在容量章节详细介绍),对于列表数量不多的情况,可以用sorted set类型来实现,比如上文中举例的打擂金曲排行榜,每天全国只有一份,两种数据类型的内存容量差距可以忽略不计,但是如果要实现某首歌曲的翻唱作品地区排行榜,数百万的歌曲,300多个地区,会产生数量庞大的榜单,或者数量更加庞大的朋友圈点赞列表,就需要慎重地考虑容量的问题了

    在redis里面,我们可以把list玩成,栈, 队列, 阻塞队列!所有的list命令都是用l开头的

    127.0.0.1:6379> lpush list one   //将一个值或多个值,插入列表头部
    (integer) 1
    127.0.0.1:6379> lpush list two
    (integer) 2 
    127.0.0.1:6379> lpush list threellen list
    (integer) 3
    127.0.0.1:6379> rpush list righr //将一个或多个值放入尾部
    (integer) 4
    127.0.0.1:6379> lrange list 0 -1
    1) "three"
    2) "two"
    3) "one"
    4) "righr"
    127.0.0.1:6379> lpop list //移除list的第一个元素
    "three"
    127.0.0.1:6379> lrange list 0 -1
    1) "two"
    2) "one"
    3) "righr"
    127.0.0.1:6379> rpop list//移除list的最后一个元素
    "righr"
    127.0.0.1:6379> lrange list 0 -1
    1) "two"
    2) "one"
    127.0.0.1:6379> lindex list 1  //通过下标获取值
    "one"
    127.0.0.1:6379> lindex list 0
    "two"
    127.0.0.1:6379> llen list  //返列表的长度
    127.0.0.1:6379> lrem list 2 three   //移除指定内容的value 2个
    (integer) 2
    127.0.0.1:6379> ltrim list 1 3   //通过下标截取指定的长度。这个list已经被改变了,只剩下截取的元素
    127.0.0.1:6379> rpoplpush mylist myotherlist //移除列表的最后一个元素加到新的列表中
    127.0.0.1:6379> exists list
    127.0.0.1:6379> lset list 0 item  //需要列表存在, 将列表中指定下标的值替换为另一个值,更新操作
    127.0.0.1:6379> lpush list value1
    127.0.0.1:6379> lset list 0 item  //如果不存在列表就报错
    127.0.0.1:6379> rpush mylist "hello"
    (integer) 1
    127.0.0.1:6379> rpush mylist "world"
    (integer) 2
    127.0.0.1:6379> linsert mylist before "word" "other" //将某个具体的value插入到列表中某个元素的前边或后边
    (integer) -1
    127.0.0.1:6379> linsert mylist before "world" "other"
    (integer) 3
    127.0.0.1:6379> lrange mylist 0 -1
    1) "hello"
    2) "other"
    3) "world"
    127.0.0.1:6379> linsert mylist after world new
    (integer) 4
    127.0.0.1:6379> lrange mylist 0 -1
    1) "hello"
    2) "other"
    3) "world"
    4) "new"
    • 他实际上是一个链表, before Node after, left right都可以插入值
    • 如果key不存在, 创建新的链表
    • 如果key存在,新增内容
    • 如果移除了所有值, 空链表,也代表不存在
    • 在两边插入或者改动值,效率最高, 中间元素,相对来说效率会低一咪

    root