【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 命令的『循环列表』模式

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

[笔试强训day06]

文章目录 NC10 大数乘法NC1 大数加法NC40 链表相加(二) NC10 大数乘法 NC10 大数乘法 #include <string> #include <vector> class Solution {public:string solve(string s, string t) {int m s.size(), n t.size();reverse(s.begin(), s.end());reverse(t.beg…

Matlab图像处理——基于BP神经网络的车牌标识识别系统

1. 数据集介绍 中国交通标志数据集&#xff1a; https://nlpr.ia.ac.cn/pal/trafficdata/detection.html 该数据集包含58类交通标志。 2. 数据处理 按照文件标签&#xff0c;将数据集划分了58类&#xff0c;如下&#xff1a; 对应的类别信息记录如下&#xff1a; 限速5km/…

2024 一带一路暨金砖国家技能发展与技术创新大赛【企业信息系统安全赛项】选拔赛样题

2024 一带一路暨金砖国家技能发展与技术创新大赛--企业信息系统安全赛项任务书 第一阶段&#xff1a; CTF 夺旗任务一 WEBCMS任务二 杂项 MISC任务三 Linux 应急响应分析任务四 Baby_PWN任务五 流量溯源 第二阶段&#xff1a; 企业网络安全配置与渗透需要2023环境私信博主&…

element-plus中使用el-switch时,用‘0,1’或者0,1来代替true,false绑定

介绍 switch 开关默认用 true, false来绑定的&#xff0c;但是在实际的项目中&#xff0c;有时候根据后端的接口返回&#xff0c;也可能会用字符串0 和 1 &#xff0c;或者数字 0,1来代替; 具体实现如下 详情&#xff1a; 主要实现方式是通过使用el-switch组件里的 active-val…

企业计算机服务器中了rmallox勒索病毒怎么处理,rmallox勒索病毒处理建议

在网络技术不断发展的时代&#xff0c;网络在企业中的应用广泛&#xff0c;可以为企业带来更多的便利&#xff0c;大大提升了企业的生产效率&#xff0c;但网络作为虚拟世界&#xff0c;在为企业提供便利的同时&#xff0c;也为企业数据安全带来严重威胁。近期&#xff0c;云天…

mysql 删除数据,导致存在表空间碎片的解决方法

mysql删除数据&#xff0c;导致存在表空间碎片的解决方法 1.分区表2. 使用OPTIMIZE TABLE3. 定期重建表4. 监控和维护5. 考虑其他存储引擎或数据库系统&#xff1a;6. 调整删除策略&#xff1a; 删除大量数据&#xff0c;尤其是频繁发生的删除操作&#xff0c;确实可能导致表空…

ffmpeg命令行工具安装

1. root用户安装 #!/bin/bash sudo yum install epel-release -y#由于CentOS没有官方FFmpeg rpm软件包。但是&#xff0c;我们可以使用第三方YUM源&#xff08;Nux Dextop&#xff09;完成此工作。--外网 sudo rpm --import http://li.nux.ro/download/nux/RPM-GPG-KEY-nux.ro…

【YOLO改进】换遍IoU损失函数之EIoU Loss(基于MMYOLO)

EIoU损失函数 设计原理 一、IoU的局限性 IoU&#xff08;Intersection over Union&#xff09;是一种常用于评估目标检测模型性能的指标&#xff0c;特别是在计算预测边界框与真实边界框之间的重叠程度时。然而&#xff0c;IoU存在一些局限性&#xff0c;尤其是当两个边界框…

[python趣味实战]----基于python代码实现浪漫爱心 დ

正文 01-效果演示 下图是代码运行之后的爱心显示结果&#xff1a; 下面的视频该爱心是动态效果&#xff0c;较为简洁&#xff0c;如果需要使用&#xff0c;可以进行完善&#xff0c;这里只是一个趣味实战&#xff0c;下面将对代码实现进行非常详细地描述&#xff1a; 浪漫爱心…

Java数据结构-模拟实现ArrayList

MyArrayList顺序结构&#xff1a; 接口和MyArrayList重写接口 接口 接口中的方法是很多类通用的&#xff0c;所以可以写到接口中 public interface IList {public void add(int data) ;// 在 pos 位置新增元素public void add(int pos, int data);// 判定是否包含某个元素p…

踏上R语言之旅:解锁数据世界的神秘密码(三)

多元相关与回归分析及R使用 文章目录 多元相关与回归分析及R使用一.变量间的关系分析1.两变量线性相关系数的计算2.相关系数的假设检验 二.一元线性回归分析的R计算三、回归系数的假设检验总结 一.变量间的关系分析 变量间的关系及分析方法如下&#xff1a; 1.两变量线性相关…

LeetCode 727. 菱形

输入一个奇数 n n n&#xff0c;输出一个由 * 构成的 n n n阶实心菱形。 输入格式 一个奇数 n n n。 输出格式 输出一个由 * 构成的 n n n阶实心菱形。 具体格式参照输出样例。 数据范围 1 ≤ n ≤ 99 1≤n≤99 1≤n≤99 输入样例&#xff1a; 5输出样例&#xff1a; *…

JAVA的多态

在Java中&#xff0c;多态&#xff08;Polymorphism&#xff09;是面向对象编程的三大特性之一&#xff0c;它允许一个引用变量在运行时引用不同类的对象&#xff0c;并根据实际对象的类型来执行对应的方法。多态的存在增加了代码的灵活性和可扩展性。 多态的实现通常依赖于以下…

一文掌握python上下文管理器(with语句)

目录 一、上下文管理协议 二、with 语句 三、自定义上下文管理器 四、生成器上下文管理器 五、几个常用例子 1、自动关闭网络连接 2、临时更改目录 3、数据库事务管理 4、计时器上下文管理器 5、日志记录上下文管理器 6、资源锁定上下文管理器 7、临时修改环境变量…

windows远程访问树莓派ubuntu22.04 桌面 - NoMachine

通过nomachine 实现 windows 安装 nomachine 下载&#xff1a;链接&#xff1a;https://pan.baidu.com/s/10rGBREs-AnwRz7D7QbLQ1A?pwd8651 提取码&#xff1a;8651 安装&#xff1a;下一步 下一步 使用&#xff1a; 下一步 下一步 ubuntu 安装 nomachine服务 下载&#…

Java基础知识总结(81)

JUC容器 JUC基于非阻塞算法&#xff08;Lock Free 无锁编程&#xff09;提供了一组高并发的List、Set、Queue、Map容器。 JUC高并发容器是基于非阻塞算法实现的容器类&#xff0c;无锁编程算法主要通过CAS&#xff08;Compare And Swap&#xff09;volatile的组合实现&#x…

【C++程序员的自我修炼】string 库中常见的用法 (一)

唤起一天明月照我满怀冰雪浩荡百川流鲸饮未吞海 剑气已横秋 目录 string 库的简介 string 的一些小操作 构造函数的使用 拷贝构造的常规使用 指定拷贝内容的拷贝构造 拷贝字符串开始的前 n 个字符 用 n 个字符初始化 计算字符串的长度 string 的三种遍历方式 常规的for循环 op…

利用大型语言模型提升数字产品创新:提示,微调,检索增强生成和代理的应用

每周跟踪AI热点新闻动向和震撼发展 想要探索生成式人工智能的前沿进展吗&#xff1f;订阅我们的简报&#xff0c;深入解析最新的技术突破、实际应用案例和未来的趋势。与全球数同行一同&#xff0c;从行业内部的深度分析和实用指南中受益。不要错过这个机会&#xff0c;成为AI领…

Linux基础part-6

一、Shell编程理论和运用 1、程序的编程风格和执行模式 编程风格&#xff08;Programming Style&#xff09; 过程式编程&#xff1a;以指令为中心&#xff0c;来进行写程序&#xff0c;数据服务于指令。&#xff08;bash shell) C 以指令为中心&#xff0c;程序的逻辑由一系…

「笔试刷题」:字母收集

一、题目 描述 有一个 &#x1d45b;∗&#x1d45a; 的矩形方阵&#xff0c;每个格子上面写了一个小写字母。 小红站在矩形的左上角&#xff0c;她每次可以向右或者向下走&#xff0c;走到某个格子上就可以收集这个格子的字母。 小红非常喜欢 "love" 这四个字母。…