【redis】Redis数据类型(三)List类型

目录

  • List类型介绍
    • 特点
  • List数据结构
    • 附:3.2以前的版本(介绍一下压缩列表和双向链表)
      • 压缩列表ZipList
      • 双向链表LinkedList
  • 常用命令
      • lpush
        • 示例
      • lpushx
        • 示例
      • rpush
        • 示例
      • rpushx
        • 示例
      • LPOP
        • 示例
      • RPOP
        • 示例
      • BLPOP
        • 非阻塞行为
        • 阻塞行为
        • 相同的 key 被多个客户端同时阻塞
        • 在 MULTI/EXEC 事务中的 BLPOP
      • BRPOP
        • 示例
      • LLEN
        • 示例
      • LRANGE
        • 注意 LRANGE 命令和编程语言区间函数的区别
        • 超出范围的下标
        • 示例
      • LREM
        • 示例
      • LSET
        • 示例
      • LTRIM
        • 注意 LTRIM 命令和编程语言区间函数的区别
        • 超出范围的下标
        • 示例
      • LINDEX
        • 示例
      • LINSERT
        • 示例
      • RPOPLPUSH
        • 示例
        • 应用1:安全的队列
        • 应用2:循环列表
      • BRPOPLPUSH
        • 示例
        • 应用1:安全队列
        • 应用2:循环列表

List类型介绍

  • 单键多值:Redis 列表是简单的字符串列表,按照插⼊顺序排序。
  • 你可以添加⼀个元素到列表的头部(左边)或者尾部(右边)。
  • 它的底层实际是个双向链表,对两端的操作性能很⾼,通过索引下标的操作中间的节点性能会较差。
  • Redis 中列表(List)类型是用来存储多个有序的字符串,列表中的每个字符串成为元素Eelement),一个列表最多可以存储 2^32-1 个元素。
  • 在 Redis 中,可以对列表两端插入(push)和弹出(pop),还可以获取指定范围的元素列表、获取指定索引下标的元素等。列表是一种比较灵活的数据结构,可以充当栈和队列的角色,在实际开发中有很多应用场景。

特点

  • 列表中的元素是有序的,即可以通过索引下标获取某个元素或者某个范围内的元素列表;
  • 列表中的元素可以是重复的
    在这里插入图片描述

List数据结构

  • Redis3.2 版本开始,List 类型数据使用的底层数据结构是快速链表,快速列表是以压缩列表为节点的双向链表,将双向链表按段切分,每一段使用压缩列表进行内存的连续存储,多个压缩列表通过 prev 和 next 指针组成的双向链。

  • ⾸先在列表元素较少的情况下会使⽤⼀块连续的内存存储,这个结构是 ziplist,也即是压缩列表。它将所有的元素紧挨着⼀起存储,分配的是⼀块连续的内存。

  • 当数据量⽐较多的时候才会改成 quicklist。因为普通的链表需要的附加指针空间太⼤,会⽐较浪费空间。⽐如这个列表⾥存的只是 int 类型的数据,结构上还需要两个额外的指针 prev 和 next。
    在这里插入图片描述

  • Redis 将链表和 ziplist 结合起来组成了 quicklist。也就是将多个 ziplist 使⽤双向指针串起来使⽤。这样既满⾜了快速的插⼊删除性能,⼜不会出现太⼤的空间冗余。

在这里插入图片描述
考虑到链表的以上缺点,Redis 后续版本对列表数据结构进行改造,使用 QucikList 代替了 ZipList 和 LinkedList。 作为 ZipList 和 LinkedList 的混合体,它将 LinkedList 按段切分,每一段使用 ZipList 来紧凑存储,多个 ZipList 之间使用双向指针串接起来。

附:3.2以前的版本(介绍一下压缩列表和双向链表)

压缩列表ZipList

  • 压缩列表是一块连续的内存空间 (像内存连续的数组,但每个元素长度不同),一个 ziplist 可以包含多个节点(entry)。元素之间紧挨着存储,没有任何冗余空隙。
    在这里插入图片描述
  • 压缩列表的本质就是一个数组,只不过是增加了 “列表长度”、“尾部偏移量”、“列表元素个数” 以及 “列表结束标识”,这样的话就有利于快速的寻找列表的首、尾节点.压缩列表将表中每一项存放在前后连续的地址空间内,每一项因占用的空间不同,而采用变长编码。由于内存是连续分配的,所以遍历速度很快。
  • 当我们的 List 列表数据量比较少的时候,且存储的数据轻量的(如小整数值、短字符串)时候, Redis 就会通过压缩列表来进行底层实现。

双向链表LinkedList

  • LinkedList 是标准的双向链表,Node 节点包含 prev 和 next 指针,分别指向后继与前驱节点,因此从双向链表中的任意一个节点开始都可以很方便地访问其前驱与后继节点。
    在这里插入图片描述

  • LinkedList 可以进行双向遍历;添加删除元素快 O(1),查找元素慢 O(n),高效实现了 LPUSH 、RPOP、RPOPLPUSH,但由于需要为每个节点分配额外的内存空间,所以会浪费一定的内存空间。这种编码方式适用于元素数量较多或者元素较大的场景。

  • LinkedList 结构为链表提供了表头指针 head、表尾指针 tail,以及节点数量计算 len。下图展示一个由 list 结构和三个 listNode 节点组成的链表:
    在这里插入图片描述

  • Redis 的链表实现的特性可以总结如下:

    • 双端:链表节点带有 prev 和 next 指针,获取某个节点的前一节点和后一节点的复杂度都是 O(1);
    • 无环:表头节点的 prev 指针和表尾节点的 next 指针都指向 NULL,对链表的访问以 NULL 为终点;
    • 表头指针/表尾指针:通过 list 结构的 head 指针和 tail 指针,获取链表的表头节点和表尾节点的复杂度为 O(1);
    • 链表长度计数器:通过 list 结构的 len 属性来对 list 的链表节点进行计数,获取节点数量的复杂度为O(1);
    • 多态:链表节点使用 void* 指针来保存节点值,并通过 list 结构的 dup、free、match 三个属性为节点值设置类型特定函数,所以链表可以用于保存各种不同类型的值。
    • 使用链表的附加空间相对太高,因为 64bit 系统中指针是 8 个字节,所以 prev 和 next 指针需要占据 16 个字节,且链表节点在内存中单独分配,会加剧内存的碎片化,影响内存管理效率

常用命令

  • lpush/rpush <key><value1><value2><value3> … 从左边/右边插⼊⼀个或多个值。
  • lpop/rpop <key> 从左边/右边吐出⼀个值。值在键在,值光键亡。
  • rpoplpush <key1><key2> 从 <key1> 列表右边吐出⼀个值,插到 <key2> 列表左边。
  • lrange <key><start><stop> 按照索引下标获得元素(从左到右)
  • lrange mylist 0 -1 0左边第⼀个,-1右边第⼀个,(0-1表示获取所有)
  • lindex <key><index> 按照索引下标获得元素(从左到右)
  • llen <key> 获得列表⻓度
  • linsert <key> before <value><newvalue> 在 <value> 的后⾯插⼊值 <newvalue>
  • lrem <key><n><value> 从左边删除 n 个 value (从左到右)
  • lset <key><index><value> 将列表 key 下标为 index 的值替换成 value

lpush

  • 语法:lpush key value [value …]
  • 解释:
    • 将一个或多个值 value 插入到列表 key 的表头
    • 如果有多个 value 值,那么各个 value 值按从左到右的顺序依次插入到表头:比如说,对空列表 mylist 执行命令 LPUSH mylist a b c ,列表的值将是 c b a ,这等同于原子性地执行 LPUSH mylist a 、 LPUSH mylist b 和 LPUSH mylist c 三个命令。
    • 如果 key 不存在,一个空列表会被创建并执行 LPUSH 操作。
    • 当 key 存在但不是列表类型时,返回一个错误。
    • 注: 在 Redis 2.4 版本以前的 LPUSH 命令,都只接受单个 value 值。
  • 时间复杂度:O(1)
  • 返回值:执行 LPUSH 命令后,列表的长度。
示例
# 加入单个元素
127.0.0.1:6379[3]> LPUSH languages python
(integer) 1
# 加入重复元素
127.0.0.1:6379[3]> LPUSH languages python
(integer) 2
127.0.0.1:6379[3]> LRANGE languages 0 -1 # 列表允许重复元素
1) "python"
2) "python"
# 加入多个元素
127.0.0.1:6379[3]> LPUSH mylist a b c
(integer) 3
127.0.0.1:6379[3]> LRANGE mylist 0 -1
1) "c"
2) "b"
3) "a"

lpushx

  • 语法:lpushx key value
  • 解释:
    • 将值 value 插入到列表 key 的表头,当且仅当 key 存在并且是一个列表。
    • 和 LPUSH 命令相反,当 key 不存在时, LPUSHX 命令什么也不做
  • 时间复杂度:O(1)
  • 返回值:LPUSHX 命令执行之后,表的长度
示例
# 对空列表执行 LPUSHX
127.0.0.1:6379[3]> LLEN greet # greet 是一个空列表
(integer) 0
127.0.0.1:6379[3]> LPUSHX greet "hello" # 尝试 LPUSHX,失败,因为列表为空
(integer) 0
# 对非空列表执行 LPUSHX
127.0.0.1:6379[3]> LPUSH greet "hello" # 先用 LPUSH 创建一个有一个元素的列表
(integer) 1
127.0.0.1:6379[3]> LPUSHX greet "good morning" # 这次 LPUSHX 执行成功
(integer) 2
127.0.0.1:6379[3]> LRANGE greet 0 -1
1) "good morning"
2) "hello"

rpush

  • 语法:rpush key value [value …]
  • 解释:
    • 将一个或多个值 value 插入到列表 key 的表尾(最右边)。
    • 如果有多个 value 值,那么各个 value 值按从左到右的顺序依次插入到表尾:比如对一个空列表 mylist 执行 RPUSH mylist a b c ,得出的结果列表为 a b c ,等同于执行命令 RPUSH mylist a RPUSH mylist b RPUSH mylist c
    • 如果 key 不存在,一个空列表会被创建并执行 RPUSH 操作。
    • 当 key 存在但不是列表类型时,返回一个错误。
    • 注:在 Redis 2.4 版本以前的 RPUSH 命令,都只接受单个 value 值。
  • 时间复杂度:O(1)
  • 返回值:执行 RPUSH 操作后,表的长度。
示例
# 添加单个元素
127.0.0.1:6379[3]> RPUSH languages c
(integer) 1
# 添加重复元素
127.0.0.1:6379[3]> RPUSH languages c
(integer) 2
127.0.0.1:6379[3]> LRANGE languages 0 -1 # 列表允许重复元素
1) "c"
2) "c"
# 添加多个元素
127.0.0.1:6379[3]> RPUSH mylist a b c
(integer) 3
127.0.0.1:6379[3]> LRANGE mylist 0 -1
1) "a"
2) "b"
3) "c"

rpushx

  • 语法:rpushx key value
  • 解释:
    • 将值 value 插入到列表 key 的表尾,当且仅当 key 存在并且是一个列表。
    • 和 RPUSH 命令相反,当 key 不存在时, RPUSHX 命令什么也不做。
  • 时间复杂度:O(1)
  • 返回值:RPUSHX 命令执行之后,表的长度
示例
# key 不存在
127.0.0.1:6379[3]> LLEN greet
(integer) 0
127.0.0.1:6379[3]> RPUSHX greet "hello" # 对不存在的 key 进行 RPUSHX,PUSH 失败。
(integer) 0
# key 存在且是一个非空列表
127.0.0.1:6379[3]> RPUSH greet "hi" # 先用 RPUSH 插入一个元素
(integer) 1
127.0.0.1:6379[3]> RPUSHX greet "hello" # greet 现在是一个列表类型,RPUSHX 操作
成功。
(integer) 2
127.0.0.1:6379[3]> LRANGE greet 0 -1
1) "hi"
2) "hello"

LPOP

  • 语法:lpop key
  • 解释:移除并返回列表 key 的头元素。
  • 时间复杂度: O(1)
  • 返回值:
    • 列表的头元素。
    • 当 key 不存在时,返回 nil 。
示例
127.0.0.1:6379[3]> LLEN course
(integer) 0
127.0.0.1:6379[3]> RPUSH course algorithm001
(integer) 1
127.0.0.1:6379[3]> RPUSH course c++101
(integer) 2
127.0.0.1:6379[3]> LPOP course # 移除头元素
"algorithm001"

RPOP

  • 语法:rpop key
  • 解释:移除并返回列表 key 的尾元素。
  • 时间复杂度: O(1)
  • 返回值:
    • 列表的尾元素。
    • 当 key 不存在时,返回 nil 。
示例
127.0.0.1:6379[3]> RPUSH mylist "one"
(integer) 1
127.0.0.1:6379[3]> RPUSH mylist "two"
(integer) 2
127.0.0.1:6379[3]> RPUSH mylist "three"
(integer) 3
127.0.0.1:6379[3]> RPOP mylist # 返回被弹出的元素
"three"
127.0.0.1:6379[3]> LRANGE mylist 0 -1 # 列表剩下的元素
1) "one"
2) "two"

BLPOP

  • 语法:blpop key [key …] timeout
  • 解释:
    • BLPOP 是列表的阻塞式(blocking)弹出原语。
    • 它是 LPOP 命令的阻塞版本,当给定列表内没有任何元素可供弹出的时候,连接将被BLPOP 命令阻塞,直到等待超时或发现可弹出元素为止。
    • 当给定多个 key 参数时,按参数 key 的先后顺序依次检查各个列表,弹出第一个非空列表的头元素。
非阻塞行为
  • 当 BLPOP 被调用时,如果给定 key 内至少有一个非空列表,那么弹出遇到的第一个非空列表的头元素,并和被弹出元素所属的列表的名字一起,组成结果返回给调用者。

  • 当存在多个给定 key 时, BLPOP 按给定 key 参数排列的先后顺序,依次检查各个列表。

  • 假设现在有 job 、 command 和 request 三个列表,其中 job 不存在, command 和request 都持有非空列表。考虑以下命令:BLPOP job command request 0

  • BLPOP 保证返回的元素来自 command ,因为它是按”查找 job -> 查找 command -> 查找 request “这样的顺序,第一个找到的非空列表。

    127.0.0.1:6379[3]> DEL job command request # 确保 key 都被删除
    (integer) 0
    127.0.0.1:6379[3]> LPUSH command "update system..." # 为 command 列表增加一个值
    (integer) 1
    127.0.0.1:6379[3]> LPUSH request "visit page" # 为 request 列表增加一个值
    (integer) 1
    127.0.0.1:6379[3]> BLPOP job command request 0 # job 列表为空,被跳过,紧接着command 列表的第一个元素被弹出。
    1) "command" # 弹出元素所属的列表
    2) "update system..." # 弹出元素所属的值
    
阻塞行为
  • 如果所有给定 key 都不存在或包含空列表,那么 BLPOP 命令将阻塞连接,直到等待超时,或有另一个客户端对给定 key 的任意一个执行 LPUSH 或 RPUSH 命令为止。

  • 超时参数 timeout 接受一个以秒为单位的数字作为值。超时参数设为 0 表示阻塞时间可以无限期延长(block indefinitely) 。

    127.0.0.1:6379[3]> EXISTS job # 确保两个 key 都不存在
    (integer) 0
    127.0.0.1:6379[3]> EXISTS command
    (integer) 0
    127.0.0.1:6379[3]> BLPOP job command 300 # 因为 key 一开始不存在,所以操作会被阻塞,直到另一客户端对 job 或者 command 列表进行 PUSH 操作。
    1) "job" # 这里被 push 的是 job
    2) "do my home work" # 被弹出的值
    (26.26s) # 等待的秒数
    127.0.0.1:6379[3]> BLPOP job command 5 # 等待超时的情况
    (nil)
    (5.66s) # 等待的秒数
    
相同的 key 被多个客户端同时阻塞
  • 相同的 key 可以被多个客户端同时阻塞。
  • 不同的客户端被放进一个队列中,按『先阻塞先服务』(first-BLPOP,first-served)的顺序为 key 执行 BLPOP 命令。
在 MULTI/EXEC 事务中的 BLPOP
  • BLPOP 可以用于流水线(pipline,批量地发送多个命令并读入多个回复),但把它用在MULTI / EXEC 块当中没有意义。因为这要求整个服务器被阻塞以保证块执行时的原子性,该行为阻止了其他客户端执行 LPUSH 或 RPUSH 命令。

  • 因此,一个被包裹在 MULTI / EXEC 块内的 BLPOP 命令,行为表现得就像 LPOP 一样,对空列表返回 nil ,对非空列表弹出列表元素,不进行任何阻塞操作。

    # 对非空列表进行操作
    127.0.0.1:6379[3]> RPUSH job programming
    (integer) 1
    127.0.0.1:6379[3]> MULTI
    OK
    127.0.0.1:6379[3]> BLPOP job 30
    QUEUED
    127.0.0.1:6379[3]> EXEC # 不阻塞,立即返回
     1) "job"
     2) "programming"
    # 对空列表进行操作
    127.0.0.1:6379[3]> LLEN job # 空列表
    (integer) 0
    127.0.0.1:6379[3]> MULTI
    OK
    127.0.0.1:6379[3]> BLPOP job 30
    QUEUED
    127.0.0.1:6379[3]> EXEC # 不阻塞,立即返回
    1) (nil)
    
  • 时间复杂度: O(1)

  • 返回值:

    • 如果列表为空,返回一个 nil 。
    • 否则,返回一个含有两个元素的列表,第一个元素是被弹出元素所属的 key ,第二个元素是被弹出元素的值

BRPOP

  • 语法:brpop key [key …] timeout
  • 解释:
    • BRPOP 是列表的阻塞式(blocking)弹出原语。
    • 它是 RPOP 命令的阻塞版本,当给定列表内没有任何元素可供弹出的时候,连接将被BRPOP 命令阻塞,直到等待超时或发现可弹出元素为止。
    • 当给定多个 key 参数时,按参数 key 的先后顺序依次检查各个列表,弹出第一个非空列表的尾部元素。
    • 关于阻塞操作的更多信息,请查看 BLPOP 命令, BRPOP 除了弹出元素的位置和 BLPOP 不同之外,其他表现一致。
  • 时间复杂度:O(1)
  • 返回值:
    • 假如在指定时间内没有任何元素被弹出,则返回一个 nil 和等待时长。
    • 反之,返回一个含有两个元素的列表,第一个元素是被弹出元素所属的 key ,第二个元素是被弹出元素的值。
示例
127.0.0.1:6379[3]> LLEN course
(integer) 0
127.0.0.1:6379[3]> RPUSH course algorithm001
(integer) 1
127.0.0.1:6379[3]> RPUSH course c++101
(integer) 2
127.0.0.1:6379[3]> BRPOP course 30
1) "course" # 弹出元素的 key
2) "c++101" # 弹出元素的值

LLEN

  • 语法:llen key
  • 解释:
    • 返回列表 key 的长度。
    • 如果 key 不存在,则 key 被解释为一个空列表,返回 0 .
    • 如果 key 不是列表类型,返回一个错误。
  • 时间复杂度:O(1)
  • 返回值:列表 key 的长度。
示例
# 空列表
127.0.0.1:6379[3]> LLEN job
(integer) 0
# 非空列表
127.0.0.1:6379[3]> LPUSH job "cook food"
(integer) 1
127.0.0.1:6379[3]> LPUSH job "have lunch"
(integer) 2
127.0.0.1:6379[3]> LLEN job
(integer) 2

LRANGE

  • 语法:lrange key start stop
  • 解释:
    • 返回列表 key 中指定区间内的元素,区间以偏移量 start 和 stop 指定。
    • 下标(index)参数 start 和 stop 都以 0 为底,也就是说,以 0 表示列表的第一个元素,以 1 表示列表的第二个元素,以此类推。
    • 你也可以使用负数下标,以 -1 表示列表的最后一个元素, -2 表示列表的倒数第二个元素,以此类推。
注意 LRANGE 命令和编程语言区间函数的区别
  • 假如你有一个包含一百个元素的列表,对该列表执行 LRANGE list 0 10 ,结果是一个包含 11 个元素的列表,这表明 stop 下标也在 LRANGE 命令的取值范围之内(闭区间),这和某些语言的区间函数可能不一致,比如 Ruby 的 Range.new 、 Array#slice 和 Python的 range() 函数。
超出范围的下标
  • 超出范围的下标值不会引起错误。
  • 如果 start 下标比列表的最大下标 end ( LLEN list 减去 1 )还要大,或者 start > stop , LRANGE 返回一个空列表。
  • 如果 stop 下标比 end 下标还要大,Redis 将 stop 的值设置为 end 。
  • 时间复杂度:O(S+N), S 为偏移量 start , N 为指定区间内元素的数量。
  • 返回值:一个列表,包含指定区间内的元素。
示例
# 空列表
127.0.0.1:6379[3]> RPUSH fp-language lisp
(integer) 1
127.0.0.1:6379[3]> LRANGE fp-language 0 0
1) "lisp"
127.0.0.1:6379[3]> RPUSH fp-language scheme
(integer) 2
127.0.0.1:6379[3]> LRANGE fp-language 0 1
1) "lisp"
2) "scheme"

LREM

  • 语法:lrem key count value
  • 解释:
    • 根据参数 count 的值,移除列表中与参数 value 相等的元素。
    • count 的值可以是以下几种:
      • count > 0 : 从表头开始向表尾搜索,移除与 value 相等的元素,数量为 count 。
      • count < 0 : 从表尾开始向表头搜索,移除与 value 相等的元素,数量为 count 的绝对值。
      • count = 0 : 移除表中所有与 value 相等的值。
  • 时间复杂度:O(N), N 为列表的长度。
  • 返回值:被移除元素的数量。因为不存在的 key 被视作空表(empty list),所以当 key 不存在时, LREM 命令总是返回 0 。
示例
# 空列表
# 先创建一个表,内容排列是
# morning hello morning helllo morning
127.0.0.1:6379[3]> LPUSH greet "morning"
(integer) 1
127.0.0.1:6379[3]> LPUSH greet "hello"
(integer) 2
127.0.0.1:6379[3]> LPUSH greet "morning"
(integer) 3
127.0.0.1:6379[3]> LPUSH greet "hello"
(integer) 4
127.0.0.1:6379[3]> LPUSH greet "morning"
(integer) 5
127.0.0.1:6379[3]> LRANGE greet 0 4 # 查看所有元素
1) "morning"
2) "hello"
3) "morning"
4) "hello"
5) "morning"
127.0.0.1:6379[3]> LREM greet 2 morning # 移除从表头到表尾,最先发现的两个 morning
(integer) 2 # 两个元素被移除
127.0.0.1:6379[3]> LLEN greet # 还剩 3 个元素
(integer) 3
127.0.0.1:6379[3]> LRANGE greet 0 2
1) "hello"
2) "hello"
3) "morning"
127.0.0.1:6379[3]> LREM greet -1 morning # 移除从表尾到表头,第一个 morning
(integer) 1
127.0.0.1:6379[3]> LLEN greet # 剩下两个元素
(integer) 2
127.0.0.1:6379[3]> LRANGE greet 0 1
1) "hello"
2) "hello"
127.0.0.1:6379[3]> LREM greet 0 hello # 移除表中所有 hello
(integer) 2 # 两个 hello 被移除
127.0.0.1:6379[3]> LLEN greet
(integer) 0

LSET

  • 语法:lset key index value
  • 解释:
    • 将列表 key 下标为 index 的元素的值设置为 value 。
    • 当 index 参数超出范围,或对一个空列表( key 不存在)进行 LSET 时,返回一个错误。
    • 关于列表下标的更多信息,请参考 LINDEX 命令。
  • 时间复杂度:对头元素或尾元素进行 LSET 操作,复杂度为 O(1)。其他情况下,为 O(N), N 为列表的长度。
  • 返回值:操作成功返回 ok ,否则返回错误信息。
示例
# 对空列表(key 不存在)进行 LSET
127.0.0.1:6379[3]> EXISTS list
(integer) 0
127.0.0.1:6379[3]> LSET list 0 item
(error) ERR no such key
# 对非空列表进行 LSET
127.0.0.1:6379[3]> LPUSH job "cook food"
(integer) 1
127.0.0.1:6379[3]> LRANGE job 0 0
1) "cook food"
127.0.0.1:6379[3]> LSET job 0 "play game"
OK
127.0.0.1:6379[3]> LRANGE job 0 0
1) "play game"
# index 超出范围
127.0.0.1:6379[3]> LLEN list # 列表长度为 1
(integer) 1
127.0.0.1:6379[3]> LSET list 3 'out of range'
(error) ERR index out of range

LTRIM

  • 语法:ltrim key start stop
  • 解释:
    • 对一个列表进行修剪(trim),就是说,让列表只保留指定区间内的元素,不在指定区间之内的元素都将被删除。
    • 举个例子,执行命令 LTRIM list 0 2 ,表示只保留列表 list 的前三个元素,其余元素全部删除。
    • 下标(index)参数 start 和 stop 都以 0 为底,也就是说,以 0 表示列表的第一个元素,以 1 表示列表的第二个元素,以此类推。
    • 你也可以使用负数下标,以 -1 表示列表的最后一个元素, -2 表示列表的倒数第二个元素,以此类推。
    • 当 key 不是列表类型时,返回一个错误。
    • LTRIM 命令通常和 LPUSH 命令或 RPUSH 命令配合使用,举个例子
      LPUSH log newest_log
      LTRIM log 0 99
      
    • 这个例子模拟了一个日志程序,每次将最新日志 newest_log 放到 log 列表中,并且只保留最新的 100 项。注意当这样使用 LTRIM 命令时,时间复杂度是 O(1),因为平均情况下,每次只有一个元素被移除。
注意 LTRIM 命令和编程语言区间函数的区别
  • 假如你有一个包含一百个元素的列表 list ,对该列表执行 LTRIM list 0 10 ,结果是一个包含 11 个元素的列表,这表明 stop 下标也在 LTRIM 命令的取值范围之内(闭区间),这和某些语言的区间函数可能不一致,比如 Ruby 的 Range.new 、 Array#slice 和 Python的 range() 函数。
超出范围的下标
  • 超出范围的下标值不会引起错误。

  • 如果 start 下标比列表的最大下标 end ( LLEN list 减去 1 )还要大,或者 start > stop , LTRIM 返回一个空列表(因为 LTRIM 已经将整个列表清空)。

  • 如果 stop 下标比 end 下标还要大,Redis 将 stop 的值设置为 end 。

  • 时间复杂度:O(N), N 为被移除的元素的数量。

  • 返回值:命令执行成功时,返回 ok 。

示例
# 一般情况下标
127.0.0.1:6379[3]> LRANGE alpha 0 -1 # 建立一个 5 元素的列表
1) "h"
2) "e"
3) "l"
4) "l"
5) "o"
127.0.0.1:6379[3]> LTRIM alpha 1 -1 # 删除索引为 0 的元素
OK
127.0.0.1:6379[3]> LRANGE alpha 0 -1 # "h" 被删除
1) "e"
2) "l"
3) "l"
4) "o"
# stop 下标比元素的最大下标要大
127.0.0.1:6379[3]> LTRIM alpha 1 10086
OK
127.0.0.1:6379[3]> LRANGE alpha 0 -1
1) "l"
2) "l"
3) "o"
# start 和 stop 下标都比最大下标要大,且 start < sotp
127.0.0.1:6379[3]> LTRIM alpha 10086 200000
OK
127.0.0.1:6379[3]> LRANGE alpha 0 -1 # 整个列表被清空,等同于 DEL alpha
(empty list or set)
# start > stop
127.0.0.1:6379[3]> LRANGE alpha 0 -1 # 在新建一个列表
1) "h"
2) "u"
3) "a"
4) "n"
5) "g"
6) "z"
127.0.0.1:6379[3]> LTRIM alpha 10086 4
OK
127.0.0.1:6379[3]> LRANGE alpha 0 -1 # 列表同样被清空
(empty list or set)

LINDEX

  • 语法:lindex key index
  • 解释:
    • 返回列表 key 中,下标为 index 的元素。
    • 下标(index)参数 start 和 stop 都以 0 为底,也就是说,以 0 表示列表的第一个元素,以 1 表示列表的第二个元素,以此类推。
    • 你也可以使用负数下标,以 -1 表示列表的最后一个元素, -2 表示列表的倒数第二个元素,以此类推。
    • 如果 key 不是列表类型,返回一个错误。
  • 时间复杂度:
    • O(N), N 为到达下标 index 过程中经过的元素数量。
    • 因此,对列表的头元素和尾元素执行 LINDEX 命令,复杂度为 O(1)。
  • 返回值:
    • 列表中下标为 index 的元素。
    • 如果 index 参数的值不在列表的区间范围内(out of range),返回 nil 。
示例
127.0.0.1:6379[3]> LPUSH mylist "World"
(integer) 1
127.0.0.1:6379[3]> LPUSH mylist "Hello"
(integer) 2
127.0.0.1:6379[3]> LINDEX mylist 0
"Hello"
127.0.0.1:6379[3]> LINDEX mylist -1
"World"
127.0.0.1:6379[3]> LINDEX mylist 3 # index 不在 mylist 的区间范围内
(nil)

LINSERT

  • 语法:linsert key BEFORE|AFTER pivot value
  • 解释:
    • 将值 value 插入到列表 key 当中,位于值 pivot 之前或之后。
    • 当 pivot 不存在于列表 key 时,不执行任何操作。
    • 当 key 不存在时, key 被视为空列表,不执行任何操作。
    • 如果 key 不是列表类型,返回一个错误。
  • 时间复杂度:O(N), N 为寻找 pivot 过程中经过的元素数量。
  • 返回值:
    • 如果命令执行成功,返回插入操作完成之后,列表的长度。
    • 如果没有找到 pivot ,返回 -1 。
    • 如果 key 不存在或为空列表,返回 0
示例
127.0.0.1:6379[3]> RPUSH mylist "Hello"
(integer) 1
127.0.0.1:6379[3]> RPUSH mylist "World"
(integer) 2
127.0.0.1:6379[3]> LINSERT mylist BEFORE "World" "There"
(integer) 3
127.0.0.1:6379[3]> LRANGE mylist 0 -1
1) "Hello"
2) "There"
3) "World"
# 对一个非空列表插入,查找一个不存在的 pivot
127.0.0.1:6379[3]> LINSERT mylist BEFORE "go" "let's"
(integer) -1 # 失败
# 对一个空列表执行 LINSERT 命令
127.0.0.1:6379[3]> EXISTS fake_list
(integer) 0
127.0.0.1:6379[3]> LINSERT fake_list BEFORE "nono" "gogogog"
(integer) 0 # 失败

RPOPLPUSH

  • 语法:rpoplpush source destination
  • 解释:
    • 命令 RPOPLPUSH 在一个原子时间内,执行以下两个动作:
      • 将列表 source 中的最后一个元素(尾元素)弹出,并返回给客户端。
      • 将 source 弹出的元素插入到列表 destination ,作为 destination 列表的的头元素。
    • 举个例子,你有两个列表 source 和 destination , source 列表有元素 a, b, c ,destination 列表有元素 x, y, z ,执行 RPOPLPUSH source destination 之后, source 列表包含元素 a, b , destination 列表包含元素 c, x, y, z ,并且元素 c 会被返回给客户端。
      • 如果 source 不存在,值 nil 被返回,并且不执行其他动作。
      • 如果 source 和 destination 相同,则列表中的表尾元素被移动到表头,并返回该元素,可以把这种特殊情况视作列表的旋转(rotation)操作。
  • 时间复杂度:O(1)
  • 返回值:被弹出的元素。
示例
# source 和 destination 不同
127.0.0.1:6379[3]> LRANGE alpha 0 -1 # 查看所有元素
1) "a"
2) "b"
3) "c"
4) "d"
127.0.0.1:6379[3]> RPOPLPUSH alpha reciver # 执行一次 RPOPLPUSH 看看
"d"
127.0.0.1:6379[3]> LRANGE alpha 0 -1
1) "a"
2) "b"
3) "c"
127.0.0.1:6379[3]> LRANGE reciver 0 -1
1) "d"
127.0.0.1:6379[3]> RPOPLPUSH alpha reciver # 再执行一次,证实 RPOP 和 LPUSH 的位置正确
"c"
127.0.0.1:6379[3]> LRANGE alpha 0 -1
1) "a"
2) "b"
127.0.0.1:6379[3]> LRANGE reciver 0 -1
1) "c"
2) "d"
# source 和 destination 相同
127.0.0.1:6379[3]> LRANGE number 0 -1
1) "1"
2) "2"
3) "3"
4) "4"
127.0.0.1:6379[3]> RPOPLPUSH number number
"4"
127.0.0.1:6379[3]> LRANGE number 0 -1 # 4 被旋转到了表头
1) "4"
2) "1"
3) "2"
4) "3"
127.0.0.1:6379[3]> RPOPLPUSH number number
"3"
127.0.0.1:6379[3]> LRANGE number 0 -1 # 这次是 3 被旋转到了表头
1) "3"
2) "4"
3) "1"
4) "2"
应用1:安全的队列
  • Redis 的列表经常被用作队列(queue),用于在不同程序之间有序地交换消息(message)。一个客户端通过 LPUSH 命令将消息放入队列中,而另一个客户端通过 RPOP 或者 BRPOP 命令取出队列中等待时间最长的消息。
  • 不幸的是,上面的队列方法是『不安全』的,因为在这个过程中,一个客户端可能在取出一个消息之后崩溃,而未处理完的消息也就因此丢失。
  • 使用 RPOPLPUSH 命令(或者它的阻塞版本 BRPOPLPUSH )可以解决这个问题:因为它不仅返回一个消息,同时还将这个消息添加到另一个备份列表当中,如果一切正常的话,当一个客户端完成某个消息的处理之后,可以用 LREM 命令将这个消息从备份表删除。
  • 最后,还可以添加一个客户端专门用于监视备份表,它自动地将超过一定处理时限的消息重新放入队列中去(负责处理该消息的客户端可能已经崩溃),这样就不会丢失任何消息了。
应用2:循环列表
  • 通过使用相同的 key 作为 RPOPLPUSH 命令的两个参数,客户端可以用一个接一个地获取列表元素的方式,取得列表的所有元素,而不必像 LRANGE 命令那样一下子将所有列表元素都从服务器传送到客户端中(两种方式的总复杂度都是 O(N))。
  • 以上的模式甚至在以下的两个情况下也能正常工作:
    • 有多个客户端同时对同一个列表进行旋转(rotating),它们获取不同的元素,直到所有元素都被读取完,之后又从头开始。
    • 有客户端在向列表尾部(右边)添加新元素。
  • 这个模式使得我们可以很容易实现这样一类系统:有 N 个客户端,需要连续不断地对一些元素进行处理,而且处理的过程必须尽可能地快。一个典型的例子就是服务器的监控程序:
    • 它们需要在尽可能短的时间内,并行地检查一组网站,确保它们的可访问性。
  • 注意,使用这个模式的客户端是易于扩展(scala)且安全(reliable)的,因为就算接收到元素的客户端失败,元素还是保存在列表里面,不会丢失,等到下个迭代来临的时候,别的客户端又可以继续处理这些元素了。

BRPOPLPUSH

  • 语法:brpoplpush source destination timeout
  • 解释:
    • BRPOPLPUSH 是 RPOPLPUSH 的阻塞版本,当给定列表 source 不为空时, BRPOPLPUSH的表现和 RPOPLPUSH 一样。
    • 当列表 source 为空时, BRPOPLPUSH 命令将阻塞连接,直到等待超时,或有另一个客户端对 source 执行 LPUSH 或 RPUSH 命令为止。
    • 超时参数 timeout 接受一个以秒为单位的数字作为值。超时参数设为 0 表示阻塞时间可以无限期延长(block indefinitely) 。
    • 更多相关信息,请参考 RPOPLPUSH 命令。
  • 时间复杂度:O(1)
  • 返回值:
    • 假如在指定时间内没有任何元素被弹出,则返回一个 nil 和等待时长。
    • 反之,返回一个含有两个元素的列表,第一个元素是被弹出元素的值,第二个元素是等待时长。
示例
# 非空列表
127.0.0.1:6379[3]> BRPOPLPUSH msg reciver 500
"hello moto" # 弹出元素的值
(3.38s) # 等待时长
127.0.0.1:6379[3]> LLEN reciver
(integer) 1
127.0.0.1:6379[3]> LRANGE reciver 0 0
1) "hello moto"
# 空列表
127.0.0.1:6379[3]> BRPOPLPUSH msg reciver 1
(nil)
(1.34s)
应用1:安全队列

参考 RPOPLPUSH 命令的『安全队列』模式。

应用2:循环列表

参考 RPOPLPUSH 命令的『循环列表』模式

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/587596.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【类型商店】字符字符串(上)

啊&#xff0c;哈喽&#xff0c;小伙伴们大家好。我是#Y清墨&#xff0c;今天呐&#xff0c;我要介绍的是字符与字符串。 导语 在我之前发的[算法材料包]C数字拆分里有提及到.size()和length()和strlen(),想看更多内容请别划走&#xff01; 一.论字符 &#xff08;1&#xff…

口袋实验室--使用AD2高效调试IIC、UART、SPI等低速接口

目录 1. 简介 2. 调试过程 2.1 简要步骤 2.2 Si5338 寄存器配置流程 2.3 AD2的基本配置 2.4 检查Si5338状态 2.5 配置Si5338寄存器 2.6 保存Si5338寄存器 3. 总结 1. 简介 使用Digilent Analog Discovery 2进行调试不仅提升了工作效率&#xff0c;而且极大地简化了常…

Golang | Leetcode Golang题解之第60题排列序列

题目&#xff1a; 题解&#xff1a; func getPermutation(n int, k int) string {factorial : make([]int, n)factorial[0] 1for i : 1; i < n; i {factorial[i] factorial[i - 1] * i}k--ans : ""valid : make([]int, n 1)for i : 0; i < len(valid); i {…

2024年第二十一届 五一杯 (C题)大学生数学建模挑战赛|数学建模完整代码+建模过程全解全析

当大家面临着复杂的数学建模问题时&#xff0c;你是否曾经感到茫然无措&#xff1f;作为2022年美国大学生数学建模比赛的O奖得主&#xff0c;我为大家提供了一套优秀的解题思路&#xff0c;让你轻松应对各种难题。 CS团队倾注了大量时间和心血&#xff0c;深入挖掘解决方案。通…

pyinstaller打包pytorch和transformers程序

记录使用pyinstaller打包含有pytorch和transformers库的程序时遇到的问题和解决方法。 环境和版本信息 操作系统&#xff1a;Windows 11 Python&#xff1a;3.10.12 pyinstaller&#xff1a;5.13.0 torch&#xff1a;2.2.2 transformers&#xff1a;4.40.1 打包过程和问…

【进收藏夹吃灰系列】算法学习指南

文章目录 [toc]分治算法 个人主页&#xff1a;丷从心 系列专栏&#xff1a;进收藏夹吃灰系列 分治算法 博客标题博客url【分治算法】【Python实现】Hanoi塔问题https://blog.csdn.net/from__2024_04_11/article/details/138093461?spm1001.2014.3001.5502

Mysql-黑马

Mysql-黑马 编写规范&#xff1a;## 一级1. 二级三级 1.Mysql概述 数据库概念mysql数据仓库 cmd启动和停止 net start mysql180 net stop mysql180备注&#xff1a;其中的mysql180是服务名 客户端连接 远程连接数据仓库 -h 主机号 -P端口号 mysql [-h 127.0.0.1] [-P 33…

CSDN如何在个人主页开启自定义模块|微信公众号

目前只有下面三种身份才具有这个功能。 VIP博客专家企业博客 栏目内容不知道怎么写HTML的&#xff0c;可以联系我帮你添加

15.Blender Eevee和Cycles渲染引擎对比

初步介绍 Eevee是实时渲染的引擎&#xff0c;会省略一些解算方式&#xff0c;尤其对光线和阴影 Cycles会考虑这些因素&#xff0c;所以会对光线和阴影的表达更加真实&#xff0c;有一个实时光线追踪的功能 Cycles渲染完之后&#xff0c;每移动一次画面&#xff0c;都会重新渲染…

【工程记录】Python爬虫入门记录(Requests BeautifulSoup)

目录 写在前面1. 环境配置2. 获取网页数据3. 解析网页数据4. 提取所需数据4.1 简单提取4.2 多级索引提取 5. 常见问题 写在前面 仅作个人学习与记录用。主要整理使用Requests和BeautifulSoup库的简单爬虫方法。在进行数据爬取时&#xff0c;请确保遵守相关法律法规和网站的服务…

git revert的使用

由于某种原因我们需要撤销掉之前某一次的修改&#xff0c;但是这个修改已经提交&#xff0c;并且后面又经历了好几轮的提交。可能如下这种情况&#xff1a; 那么此时使用git revert再合适不过啦。git revert ${commit_id}就可以将指定commit id的修改撤销&#xff0c;然后提交…

Java从菜鸟到高手①

目录 1.数据类型 2.定义变量 2.1.编码方式 2.2.布尔型变量boolean 2.3.隐式类型转化和强制类型转化 2.4类型提升 3.字符串类型 4.运算符 4.1.取余 4.2. &#xff0c;- 4.3逻辑运算&& || &#xff01; 4.4.位运算 4.5.条件运算符 1.数据类型 Java中&#…

Linux:使用匿名管道对进程池的模拟实现

目录 一、Makefile 二、processpool.cc 2.1创建通信管道和子进程 2.2控制子进程 2.3回收进程 三、task.hpp 四、完整代码 接下来我们将模拟实现一个进程池&#xff0c;进程池广泛应用与各个领域和方向&#xff0c;比如我们打开电脑后同时打开很多个进程&#xff08;也就是软…

SpringBoot对接前端传递的base64编码的图片信息,转成图片以Get请求进行浏览器文件下载,不下载到本地。

一、问题描述 1.1需求描述。 前端将浏览器展示的图片以base64编码的形式传递给后端&#xff0c;以此实现文件下载的功能&#xff0c;在浏览器弹出文件下载框。效果如下 1.2实现思路 将前端传递的base64进行解码&#xff0c;设置响应头返回响应体&#xff0c;代码如下。 pu…

如何安全的使用密码登录账号(在不知道密码的情况下)

首先&#xff0c;需要用到的这个工具&#xff1a; 度娘网盘 提取码&#xff1a;qwu2 蓝奏云 提取码&#xff1a;2r1z 1、打开工具&#xff0c;进入账号密码模块&#xff0c;如图 2、看到鼠标移动到密码那一栏有提示&#xff0c;按住Ctrl或者Alt点击或者双击就能复制内容&…

【大前端】ECharts 绘制立体柱状图

立体柱状图分为&#xff1a; 纯色立体柱状图渐变立体柱状图 常用实现方式 纯色立体柱状图 纯色立体柱状图&#xff0c;使用MarkPoint和颜色渐变就实现&#xff0c;如下代码 import * as echarts from "echarts";var chartDom document.getElementById("main&…

Window(Qt/Vs)软件添加版本信息

Window&#xff08;Qt/Vs&#xff09;软件添加版本信息 文章目录 Window&#xff08;Qt/Vs&#xff09;软件添加版本信息VS添加版本信息添加资源文件添加版本定义头自动更新版本添加批处理脚本设置生成事件 Qt添加版本信息添加资源文件文件信息修改自动更新版本 CMake添加版本信…

rust疑难杂症

rust疑难杂症解决 边碰到边记录&#xff0c;后续可能会逐步增加&#xff0c;备查 cargo build时碰到 Blocking waiting for file lock on package cache 原因是Cargo 无法获取对包缓存的文件锁&#xff0c; 有时vscode中项目比较多&#xff0c;如果其中某些库应用有问题&…

环形链表的经典问题

环形链表 环形链表的介绍链表中是否带环返回链表开始入环的第一个节点 本文主要介绍如何判断一个链表是否是环形链表&#xff0c;以及如何得到环形链表中的第一个节点。 环形链表的介绍 环形链表是一种链表数据结构&#xff0c;环形链表是某个节点的next指针指向前面的节点或指…

【linux学习指南】linux 环境搭建

文章目录 &#x1f4dd;前言&#x1f320; 云服务器的选择&#x1f320;阿里云&#x1f320;腾讯云&#x1f320;华为云 &#x1f320;使用 XShell 远程登陆到 Linux&#x1f309;下载 XShell &#x1f320;查看 Linux 主机 ip&#x1f309; XShell 下的复制粘贴&#x1f309; …