redis:列表list

Redis 中的列表数据结构支持多种操作,如 lpush/rpush 添加元素、lpop/rpop 弹出元素、lindex 获取指定下标元素、lrange 获取范围元素等。列表可用于实现栈、队列、消息队列、分页等功能,内部编码可以是 ziplist 或 linkedlist。在高并发场景下,结合 blpop/brpop 命令,列表常用于构建阻塞队列。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

列表

  • 列表的元素是有序的,这意味着通过索引下标获取某个元素或者某个范围内的元素列表
  • 列表中的元素可以是重复的

在这里插入图片描述
在这里插入图片描述

命令

命令描述返回值
lpushlpush k-name value [value …]–将一个或者多个值推入列表的右端执行 lpush 命令后,当前列表的长度
rpushrpush k-name value [value …]–将一个或者多个值推入列表的左端执行 rpush命令后,当前列表的长度
lpoplpop k-name --移除并且返回列表最右端的元素被移除的元素,当列表 key 不存在时,返回 nil
rpoprpop k-name–移除并且返回列表最左端的元素被移除的元素,当列表 key 不存在时,返回 nil
lpushxlpushx k-name value [value …]–将一个或者多个值推入已经存在了边列表的右端执行 lpushx 命令后,当前列表的长度。当且仅当列表存在时,此命令有效。如果列表不存在,返回长度0
rpushxrpushx k-name value [value …]–将一个或者多个值推入已经存在了边列表的左端执行 lpushx 命令后,当前列表的长度。当且仅当列表存在时,此命令有效。如果列表不存在,返回长度0
blpopblpop k-name [k-name …]timeout在超时时间内等待元素弹出
brpopbrpop k-name [k-name …]timeout在超时时间内等待元素弹出
rpoplpushrpoplpush source-key dest-key –从s-key列表种弹出位于最右端的元素并推入dest-key中被弹出的元素
rpoplpushrpoplpush source-key dest-key timeout–在timeout内从s-key列表种弹出位于最右端的元素并推入dest-key中被弹出的元素。
lindexlindex k-name offset –从列表种取出索引位offset的元素0为起始索引,-1为结束索引。如果指定索引值不在列表的区间范围内,返回 nil
lrangelrange k-name start end–返回索引[start, end]之内的元素0为起始索引,-1为结束索引。如果指定索引值不在列表的区间范围内,什么也不返回
ltrimltrim k-name start end–只保留索引[start, end]内的元素,其他的删除0为起始索引,-1为结束索引
llenllen k-name –返回列表的长度如果是空列表,返回0.如果不是列表返回错误
lsetlset k-name index new-value –设置索引index处的值位new-value.操作成功返回ok, 当索引参数超出范围,或对一个空列表进行 LSET 时,返回一个错误。

添加

lpush:从左边添加元素

只用
  • 将一个或多个值插入到列表头部
  • 如果 key 不存在,一个空列表会被创建并执行 LPUSH 操作
  • 当 key 存在但不是列表类型时,返回一个错误。

注意:在Redis 2.4版本以前的 LPUSH 命令,都只接受单个 value 值。

语法
redis 127.0.0.1:6379> LPUSH KEY_NAME VALUE1.. VALUEN
返回值

执行 LPUSH 命令后,列表的长度。

实例
127.0.0.1:6379> LPUSH list1 "foo"
(integer) 1
127.0.0.1:6379> LPUSH list1 "bar"
(integer) 2
127.0.0.1:6379> LRANGE list1 0 -1
1) "bar"
2) "foo"

rpush:从右边添加元素

作用
  • Redis Rpush 命令用于将一个或多个值插入到列表的尾部(最右边)。
  • 如果列表不存在,一个空列表会被创建并执行 RPUSH 操作。
  • 当列表存在但不是列表类型时,返回一个错误。

注意:在 Redis 2.4 版本以前的 RPUSH 命令,都只接受单个 value 值。

语法
redis 127.0.0.1:6379> RPUSH KEY_NAME VALUE1..VALUEN
返回值

执行 RPUSH 操作后,列表的长度。

实例
redis 127.0.0.1:6379> RPUSH mylist "hello"
(integer) 1
redis 127.0.0.1:6379> RPUSH mylist "foo"
(integer) 2
redis 127.0.0.1:6379> RPUSH mylist "bar"
(integer) 3
redis 127.0.0.1:6379> LRANGE mylist 0 -1
1) "hello"
2) "foo"
3) "bar"

linsert

作用
  • Redis Linsert 命令用于在列表的元素前或者后插入元素。
  • 当指定元素不存在于列表中时,不执行任何操作。
  • 当列表不存在时,被视为空列表,不执行任何操作。
  • 如果 key 不是列表类型,返回一个错误。
语法
LINSERT key BEFORE|AFTER pivot value

将值 value 插入到列表 key 当中,位于值 pivot 之前或之后。

返回值
  • 如果命令执行成功,返回插入操作完成之后,列表的长度。
  • 如果没有找到指定元素 ,返回 -1 。
  • 如果 key 不存在或为空列表,返回 0 。
实例
redis> RPUSH mylist "Hello"
(integer) 1
redis> RPUSH mylist "World"
(integer) 2
redis> LINSERT mylist BEFORE "World" "There"
(integer) 3
redis> LRANGE mylist 0 -1
1) "Hello"
2) "There"
3) "World"
redis> 

查找

lrange:获取指定范围内的元素列表

作用

获取指定区间内的元素

索引下标有两个特点:

  • 索引下标从左到右分别是0到N-1,从右到左是-1到-N
  • lrange的end选项包含了自身
语法
redis 127.0.0.1:6379> LRANGE key start  end
返回值

一个列表,包含指定区间内的元素。

实例
redis> RPUSH mylist "one"
(integer) 1
redis> RPUSH mylist "two"
(integer) 2
redis> RPUSH mylist "three"
(integer) 3
redis> LRANGE mylist 0 0
1) "one"
redis> LRANGE mylist -3 2
1) "one"
2) "two"
3) "three"
redis> LRANGE mylist -100 100
1) "one"
2) "two"
3) "three"
redis> LRANGE mylist 5 10
(empty list or set)
redis> 

lindex:获取指定下标的元素

  • 用于通过索引获取列表中的元素
  • 可以使用负数下标,以 -1 表示列表的最后一个元素, -2 表示列表的倒数第二个元素,以此类推。
语法
lindex key index_postion
返回值
  • 列表中下标为指定索引值的元素。
  • 如果指定索引值不在列表的区间范围内,返回 nil 。
实例
redis 127.0.0.1:6379> LPUSH mylist "World"
(integer) 1

redis 127.0.0.1:6379> LPUSH mylist "Hello"
(integer) 2

redis 127.0.0.1:6379> LINDEX mylist 0
"Hello"

redis 127.0.0.1:6379> LINDEX mylist -1
"World"

redis 127.0.0.1:6379> LINDEX mylist 3        # index不在 mylist 的区间范围内
(nil)

llen:获取列表长度

作用
  • 用于返回列表的长度
  • 如果列表 key 不存在,则 key 被解释为一个空列表,返回 0
  • 如果 key 不是列表类型,返回一个错误。
语法
llen key
返回值
  • 列表的长度
实例
redis 127.0.0.1:6379> RPUSH list1 "foo"
(integer) 1
redis 127.0.0.1:6379> RPUSH list1 "bar"
(integer) 2
redis 127.0.0.1:6379> LLEN list1
(integer) 2

删除

lpop:从左边弹出元素

作用
  • 移除并返回列表的第一个元素。
语法
lpop key
返回值
  • 列表的第一个元素。
  • 当列表 key 不存在时,返回 nil 。
实例
redis 127.0.0.1:6379> RPUSH list1 "foo"
(integer) 1
redis 127.0.0.1:6379> RPUSH list1 "bar"
(integer) 2
redis 127.0.0.1:6379> LPOP list1
"foo"

rpop:从右边弹出元素

作用
  • 用于移除列表的最后一个元素
  • 返回值为移除的元素
语法
rpop key
返回值
  • 被移除的元素。
  • 当列表不存在时,返回 nil 。
实例
redis> RPUSH mylist "one"
(integer) 1
redis> RPUSH mylist "two"
(integer) 2
redis> RPUSH mylist "three"
(integer) 3
redis> RPOP mylist
"three"
redis> LRANGE mylist 0 -1
1) "one"
2) "two"
redis> 

lrem:删除指定元素

作用

Redis Lrem 根据参数 COUNT 的值,移除列表中与参数 VALUE 相等的元素。

COUNT 的值可以是以下几种:

  • count > 0 : 从表头开始向表尾搜索,移除与 VALUE 相等的元素,数量为 COUNT 。
  • count < 0 : 从表尾开始向表头搜索,移除与 VALUE 相等的元素,数量为 COUNT 的绝对值。
  • count = 0 : 移除表中所有与 VALUE 相等的值。
语法
redis 127.0.0.1:6379> LREM key count VALUE
返回值
  • 被移除元素的数量。
  • 列表不存在时返回 0 。
实例

比如向列表元素为:a a a a a java b a,下面操作将从列表左边开始删除4个为a的元素

> lream listkey 4 a
(integer) 4
> lrange listkey 0 -1
1) "a"
2) "java"
3) "b"
4) "a"

ltrim:按照索引范围修建列表

作用
  • Redis Ltrim 对一个列表进行修剪(trim),就是说,让列表只保留指定区间内的元素,不在指定区间之内的元素都将被删除。
语法
ltrim key start stop
返回值
  • 命令执行成功时,返回 ok 。
实例
redis 127.0.0.1:6379> RPUSH mylist "hello"
(integer) 1
redis 127.0.0.1:6379> RPUSH mylist "hello"
(integer) 2
redis 127.0.0.1:6379> RPUSH mylist "foo"
(integer) 3
redis 127.0.0.1:6379> RPUSH mylist "bar"
(integer) 4
redis 127.0.0.1:6379> LTRIM mylist 1 -1   # 保留第一个元素
OK
redis 127.0.0.1:6379> LRANGE mylist 0 -1
1) "hello"
2) "foo"
3) "bar"

修改

lset:修改指定下标的元素

作用
  • 将列表 key 下标为 index 的元素的值设置为 value
  • 当 index 参数超出范围,或对一个空列表( key 不存在)进行 LSET 时,返回一个错误。
时间复杂度
  • 对头元素或尾元素进行 LSET 操作,复杂度为 O(1)。
  • 其他情况下,为 O(N), N 为列表的长度。
实例
# 对空列表(key 不存在)进行 LSET

redis> EXISTS list
(integer) 0

redis> LSET list 0 item
(error) ERR no such key


# 对非空列表进行 LSET
redis> LPUSH job "cook food"   
(integer) 1

redis> LRANGE job 0 0
1) "cook food"

redis> LSET job 0 "play game"  # 将第一个元素改为"play game"
OK

redis> LRANGE job  0 0
1) "play game"


# index 超出范围

redis> LLEN list                    # 列表长度为 1
(integer) 1

redis> LSET list 3 'out of range'
(error) ERR index out of range

阻塞操作

blpop

作用
  • 移出并获取列表的第一个元素
  • 如果列表没有元素会阻塞列表直到等待超时或发现可弹出元素为止
语法
blpop key [key ...] timeout
返回值
  • 如果列表为空,返回一个 nil 。
  • 否则,返回一个含有两个元素的列表,第一个元素是被弹出元素所属的 key ,第二个元素是被弹出元素的值。
实例
> blpop list:test 3  // 客户端最多阻塞3s后返回
(nil)
(3.10)
> blpop list:test 0 // 客户端一直阻塞直到得到了元素
1) "list:test"
2) "ele1"

注意

(1)如果是多个键,那么brpop会从左至右遍历键,一旦有一个键能弹出元素,客户端立即返回

127.0.0.1:6379> brpop list:1 list:2 list:3 0 
..阻塞..

此时另一个客户端分别向list:2和list:3插入元素:

client-lpush> lpush list:2 element2 
(integer) 1 
client-lpush> lpush list:3 element3 
(integer) 1

客户端会立即返回list:2中的element2,因为list:2最先有可以弹出的元素:

127.0.0.1:6379> brpop list:1 list:2 list:3 0 
1) "list:2" 
2) "element2_1"

(2)如果多个客户端对同一个键执行brpop,那么最先执行brpop命令的客户端可以获取到弹出的值;

客户端1:

client-1> brpop list:test 0 
...阻塞...

客户端2:

client-2> brpop list:test 0 
...阻塞...

客户端3:

client-3> brpop list:test 0 
...阻塞...

此时另一个客户端lpush一个元素到list:test列表中:

client-lpush> lpush list:test element 
(integer) 1

那么客户端1最会获取到元素,因为客户端1最先执行brpop,而客户端2 和客户端3继续阻塞:

client> brpop list:test 0 
1) "list:test" 
2) "element"

brpop

和上面的除了弹出方向不一样,使用方式一样

内部编码

列表的内部编码有两种

  • ziplist(压缩列表)
    • 当列表的元素个数小于list-max-ziplist-entries配置 (默认512个),同时列表中每个元素的值都小于list-max-ziplist-value配置时 (默认64字节),Redis会选用ziplist来作为列表的内部实现来减少内存的使
  • linkedlist(链表)
    • 当列表类型无法满足ziplist的条件时,Redis会使用 linkedlist作为列表的内部实现。

下面的示例演示了列表类型的内部编码,以及相应的变化。

1)当元素个数较少且没有大元素时,内部编码为ziplist:

127.0.0.1:6379> rpush listkey e1 e2 e3 
(integer) 3 
127.0.0.1:6379> object encoding listkey 
"ziplist

2)当元素个数超过512个,内部编码变为linkedlist:

127.0.0.1:6379> rpush listkey e4 e5 ...忽略... e512 e513 
(integer) 513 
127.0.0.1:6379> object encoding listkey 
"linkedlist"

3)或者当某个元素超过64字节,内部编码也会变为linkedlist:

127.0.0.1:6379> rpush listkey "one string is bigger than 64 byte...............    ................." 
(integer) 4 
127.0.0.1:6379> object encoding listkey 
"linkedlist"

其他:

  • Redis3.2版本提供了quicklist内部编码,简单地说它是以一个ziplist为节 点的linkedlist,它结合了ziplist和linkedlist两者的优势,为列表类型提供了一 种更为优秀的内部编码实现
  • 它的设计原理可以参考Redis的另一个作者 Matt Stancliff的博客:https://matt.sh/redis-quicklist

使用场景

  • lpush+lpop=Stack(栈)
  • lpush+rpop=Queue(队列)
  • lpsh+ltrim=Capped Collection(有限集合)
  • lpush+brpop=Message Queue(消息队列)

消息队列

如下图所示,Redis的lpush+brpop命令组合即可实现阻塞队列,

  • 生产者客户端使用lrpush从列表左侧插入元素,多个消费者客户端使用brpop命令 阻塞式的“抢”列表尾部的元素,多个客户端保证了消费的负载均衡和高可用性。
  • 如果消费者在取出元素之后立即“崩溃”,由于该消息已经被取出但是没有被正常处理,此时可以认为该消息已经丢失了,可能会导致业务数据丢失或者业务数据不一致等,此时我们了可以用rpoplpush命令使得消费者在主消息队列中取出元素之后再将其插入到一个“备份队列”中,直到消费者完成正常的逻辑处理之后再将消息从备份中删除,这样可以提供一个守护的线程。当备份中的消息过期之后,可以将消息重新放回到主消息队列中,以便其他消息可以几区去使用
    在这里插入图片描述
    在这里插入图片描述

存储任务信息

列表可以用来存储任务信息,最近浏览过的文章或者常用联系人信息,离用户最近的五家餐厅等。类似双向链表

每个用户都有属于自己的文章列表,现在需要分页展示文章列表。此时可以使用列表,因为列表不但是有序的,而且支持索引范围获取元素

1)每篇文章使用哈希结构存储,例如每篇文章有3个属性title、 timestamp、content:

hmset acticle:1 title xx timestamp 1476536196 content xxxx 
...
hmset acticle:k title yy timestamp 1476512536 content yyyy 
...

2)向用户文章列表添加文章,user:{id}:articles作为用户文章列表的键:

lpush user:1:acticles article:1 article3 
... 
lpush user:k:acticles article:5 
...

3)分页获取用户文章列表,例如下面伪代码获取用户id=1的前10篇文章:

articles = lrange user:1:articles 0 9 
for article in {articles}    
hgetall {article}

使用列表类型保存和获取文章列表会存在两个问题。

  • 第一,如果每次分页获取的文章个数较多,需要执行多次hgetall操作,此时可以考虑使用 Pipeline批量获取,或者考虑将文章数据序列化为字符串类型,使用mget批量获取。
  • 第二,分页获取文章列表时,lrange命令在列表两端性能较好,但是如果列表较大,获取列表中间范围的元素性能会变差,此 时可以考虑将列表做二级拆分,或者使用Redis3.2的quicklist内部编码实现, 它结合ziplist和linkedlist的特点,获取列表中间范围的元素时也可以高效完成。

其他

  • 关注列表、粉丝列表
  • 最新列表:获取前N个最新登录的用户列表
  • 抢购:使用redis的原子操作来实现这个“单线程”,首先我们把库存存在good_things:1这个列表中,如果有10件库存,就往列表中push10个数。抢购开始后,每来一个用户,就或从good_things:1中pop一个数,表示抢购成功,列表为空时,表示商品已经被抢完。pop操作是原子性的,此时如果有很多用户同时到达、也是一次可行的

总结:

1、lpop和rpop命令有对应的阻塞版本:BLPOP和BRPOP

  • 它们之间的不同在于当列表为空时,阻塞版本会将客户端阻塞,我们必须在这些阻塞版本的命令中指定一个以秒为单位的超时时间,表示最长等待几秒。
  • 当超时时间为零时,表示永久等待,可以用于任务调度

2、redis在内部使用quicklist存储列表对象。有两个配置选项可以调整列表对象的存储逻辑:

  • list-max-ziplist-size:一个列表条目中一个内部节点的最大大小[quicklist的每个节点都是一个ziplist]。使用默认值即可
  • list-compress-depth:该参数表示quicklist两端不被压缩的节点的个数

3、在redis里面,多个命令原子的执行指的是,在这些命令正在读取或者修改数据的时候,其他客户端不能读取或者修改相同的数据

4、对于阻塞弹出命令和弹出并推入命令,最常见用于消息传递和任务队列

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值