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

news2024/11/18 9:32:41

目录

  • 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.coloradmin.cn/o/1637285.html

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

相关文章

vscode 使用code runner 运行代码输出乱码

vscode 使用code runner 运行代码输出乱码 先指出问题所在&#xff1a; 代码文件使用的编码格式和终端使用的编码格式不一致&#xff0c;查看代码文件右下角&#xff0c;会显示代码文件的编码格式。 测试代码如下&#xff1a; #include<iostream> using namespace std…

专注 APT 攻击与防御—工具介绍-the-backdoor-factory

工具介绍 the-backdoor-factory 项目地址&#xff1a;GitHub - secretsquirrel/the-backdoor-factory: Patch PE, ELF, Mach-O binaries with shellcode new version in development, available only to sponsors 原理 可执行二进制文件中有大量的 00&#xff0c;这些 00 是…

计算机毕业设计python_django宠物领养系统z6rfy

本宠物领养系统主要包括两大功能模块&#xff0c;即管理员模块、用户模块。下面将对这两个大功能进行具体功能需求分析。 &#xff08;1&#xff09;管理员&#xff1a;管理员登录后主要功能包括个人中心、用户管理、送养宠物管理、地区类型管理、失信黑名单管理、申请领养管理…

瑞芯微-I2S | ALSA基础-3

针对音频设备&#xff0c;linux内核中包含了两类音频设备驱动框架&#xff1b; OSS&#xff1a;开放声音系统 包含dsp和mixer字符设备接口&#xff0c;应用访问底层硬件是直接通过sound设备节点实现的&#xff1b; ALSA&#xff1a;先进linux声音架构&#xff08;Advanced Lin…

bun 换源 国内阿里源 npmmirror 加速下载

Github https://github.com/oven-sh/bun 版本号 bun 1.1.5 windows 安装 bun 如果本机有 nodejs 环境, 可以 npm install -g bun 安装 ( 官方把 exe 已经传到了 npm 仓库, 走的国内 npm 镜像, 下载速度会很快) 没有 nodejs, 可以用 powershell 脚本安装 具体操作 全局 …

Python数据分析实验二:Python数据预处理

目录 一、实验目的与要求二、实验任务三、主要程序清单和运行结果&#xff08;一&#xff09;对chipotle.csv文件的销售数据进行分析&#xff08;二&#xff09;对描述泰坦尼克号成员的信息进行可视化和相关分析 四、实验体会 一、实验目的与要求 1、目的&#xff1a;   掌握…

python爬取网页趋势图的底层数据信息——以历年的黄金价格为例

一、问题引入 黄金价格网址&#xff1a;https://china.gold.org/goldhub/data/gold-prices 问题引入&#xff1a;现有历年的黄金价格信息&#xff08;如图所示&#xff09;&#xff0c;但呈现的方式是趋势图&#xff0c;并没有直接以表格的形式罗列出来&#xff0c;只有当鼠标悬…

家政服务小程序:家政行业的数字化转型

随着大众生活水平的提高&#xff0c;以及老龄化的加速&#xff0c;家政服务已经成为了大众生活中不可或缺的一部分。目前&#xff0c;我国家政服务市场的规模在持续扩大&#xff0c;发展前景一片大好。在日益提升的家政需求下&#xff0c;大众对家政服务的种类也逐渐多样。 为…

RuoYi-Vue-Plus (SPEL 表达式)

RuoYi-Vue-Plus 中SPEL使用 DataScopeType 枚举类中&#xff1a; /*** 部门数据权限*/DEPT("3", " #{#deptName} #{#user.deptId} ", " 1 0 "), PlusDataPermissionHandler 拦截器中定义了解析器&#xff1a; buildDataFilter 方法中根据注解的…

Vue 项目 尚品汇(一)

一、开发环境构造 Vue-cli 脚手架初始化项目 node 平台 和 webpack 和 淘宝镜像 环境 &#xff08;一&#xff09;脚手架 1.安装脚手架 在我们的项目文件夹中路径输入 cmd 然后在终端中输入 vue create app(项目名) 选择 vue 2 然后安装 &#xff08;因为是基于 vue2 开…

Object类的公共方法面试问题及回答

1. 什么是 Object 类&#xff1f; 答&#xff1a; Object 类是 Java 中所有类的超类。每个类都使用 Object 作为树的根&#xff0c;所有对象&#xff08;包括数组&#xff09;都实现这个类的方法。 2. Object 类中有哪些重要的方法&#xff1f; 答&#xff1a; equals(Obje…

上海开放大学《Java程序基础课程实验1》形考作业线上实践答案

答案&#xff1a;更多答案&#xff0c;请关注【电大搜题】微信公众号 答案&#xff1a;更多答案&#xff0c;请关注【电大搜题】微信公众号 答案&#xff1a;更多答案&#xff0c;请关注【电大搜题】微信公众号 上 海 开 放 大 学 学生实验报告 分校&#xff08;站&…

启明云端2.4寸屏+ESP32-S3+小型智能调速电动家用除草机案例 触控三档调速,能显示电压故障码

今天给大家分享个启明云端2.4寸屏ESP32-S3小型智能调速电动家用除草机案例&#xff0c;国外有草坪文化&#xff0c;这个机器能智能触控三档调速&#xff0c;带屏能显示电压故障码&#xff0c;数显档位&#xff08;3档最大&#xff09;&#xff0c;触控屏&#xff0c;长按3秒就能…

【氮化镓】GaN 器件的高温运行

《High Temperature Operation of E-Mode and D-Mode AlGaN/GaN MIS-HEMTs With Recessed Gates》&#xff0c;由HANWOOL LEE, HOJOON RYU, JUNZHE KANG, 和 WENJUAN ZHU (IEEE高级会员) 四位作者共同撰写&#xff0c;发表在《IEEE Journal of the Electron Devices Society》上…

和丰多媒体信息发布系统 QH.aspx 文件上传致RCE漏洞复现

0x01 产品简介 和丰多媒体信息发布系统也称数字标牌(Digital Signage),是指通过大屏幕终端显示设备,发布商业、财经和娱乐信息的多媒体专业视听系统,常被称为除纸张媒体、电台、电视、互联网之外的“第五媒体”。该系统基于Web的全B/S先进架构,支持大用户数、大并发数及…

LeetCode题练习与总结:删除排序链表中的重复元素Ⅱ--82

一、题目描述 给定一个已排序的链表的头 head &#xff0c; 删除原始链表中所有重复数字的节点&#xff0c;只留下不同的数字 。返回 已排序的链表 。 示例 1&#xff1a; 输入&#xff1a;head [1,2,3,3,4,4,5] 输出&#xff1a;[1,2,5]示例 2&#xff1a; 输入&#xff1a;…

容器的通俗讲解:轻松理解容器技术

文章目录 什么是容器&#xff1f;容器与虚拟机的区别容器如何工作&#xff1f;容器的优势容器的应用场景常见容器技术相关解决方案 在当今的软件开发领域&#xff0c;容器技术已经成为一种异常流行的技术&#xff0c;但对于初学者来说&#xff0c;容器究竟是什么以及它们如何工…

Stm32CubeMX 为 stm32mp135d 添加网卡 eth

Stm32CubeMX 为 stm32mp135d 添加网卡 eth 一、启用设备1. eth 设备添加2. eth 引脚配置2. eth 时钟配置 二、 生成代码1. optee 配置2. uboot 配置3. linux 配置 bringup 可参考&#xff1a;Stm32CubeMX 生成设备树 一、启用设备 1. eth 设备添加 我这里只启用一个eth设备&…

容器组_概述

&#x1f4d5;作者简介&#xff1a; 过去日记&#xff0c;致力于Java、GoLang,Rust等多种编程语言&#xff0c;热爱技术&#xff0c;喜欢游戏的博主。 &#x1f4d8;相关专栏Rust初阶教程、go语言基础系列、spring教程等&#xff0c;大家有兴趣的可以看一看 &#x1f4d9;Jav…

Vitis HLS 学习笔记--S_AXILITE 寄存器及驱动

目录 1. 简介 2. S_AXILITE Registers 寄存器详解 2.1 “隐式”优势 2.2 驱动程序文件 2.3 硬件头文件 2.4 硬件头文件中 SC/COR/TOW/COH 的解释 2.5 驱动控制过程 3. 总结 1. 简介 回顾此博文《Vitis HLS 学习笔记--Syn Report解读&#xff08;1&#xff09;-CSDN博…