【Redis】五大常见的数据类型之 List

简介: 我们都知道 Redis 提供了丰富的数据类型,常见的有五种:String,Hash,List,Set、Zset。今天我们就来详细的聊聊 Redis 这五大常见的数据类型之一 List;

前言

我们都知道 Redis 提供了丰富的数据类型,常见的有五种:String(字符串),Hash(哈希),List(列表),Set(集合)、Zset(有序集合)

今天我们就来详细的聊聊 Redis 这五大常见的数据类型之一 List

结构类型 结构存储的值 结构读写能力
List 一个链表,链表上的每个节点都包含一个字符串; 对链表的两端进行 pushpop 操作;
根据值查找或删除元素;

应用场景:消息队列等。

 

概述简介

List 列表是简单的字符串列表,按照插入顺序排序,可以从头部或尾部向 List 列表添加元素。

列表的最大长度为 2^32 - 1,也即每个列表支持超过 40 亿个元素。
 

内部实现

List 类型的底层数据结构是由双端链表或压缩列表实现的:

  • 如果列表的元素个数小于 512 个(默认值,可由 list-max-ziplist-entries 配置),列表每个元素的值都小于 64 字节(默认值,可由 list-max-ziplist-value 配置),Redis 会使用压缩列表作为 List 类型的底层数据结构;
  • 如果列表的元素不满足上面的条件,Redis 会使用双端链表作为 List 类型的底层数据结构;

但是在 Redis 3.2 版本之后,List 数据类型底层数据结构就只由 quicklist 实现了,替代了双端链表和压缩列表
 

常用命令

在 redis 里面,我们可以把 List 玩成栈、队列等;

# 将一个或多个值 value 插入到 key 列表的表头(最左边),最后的值在最前面
# LPUSH key element [element ...]
127.0.0.1:6379> LPUSH list a b c
(integer) 3

# 将一个或多个值 value 插入到 key 列表的表尾(最右边)
# RPUSH key element [element ...]
127.0.0.1:6379> RPUSH list d f e
(integer) 6

# 移除并返回 key 列表的头元素
# LPOP key [count]
127.0.0.1:6379> LPOP list
"c"
127.0.0.1:6379> LPOP list 2
1) "b"
2) "a"

# 移除并返回 key 列表的尾元素
# RPOP key [count]
127.0.0.1:6379> RPOP list
"e"
127.0.0.1:6379> RPOP list 2
1) "f"
2) "d"

# 返回列表 key 中指定区间内的元素,区间以偏移量 start 和 stop 指定,从0开始
# LRANGE key start stop
127.0.0.1:6379> LRANGE list 0 3
1) "a"
2) "b"
3) "c"
4) "d"

# 从 key 列表表头弹出一个元素,没有就阻塞 timeout 秒,如果 timeout=0 则一直阻塞
# BLPOP key [key ...] timeout
127.0.0.1:6379> BLPOP list 0
1) "list"
2) "b"
127.0.0.1:6379> BLPOP noList 0
# 阻塞中...

# 从 key 列表表尾弹出一个元素,没有就阻塞 timeout 秒,如果 timeout=0 则一直阻塞
# BRPOP key [key ...] timeout
127.0.0.1:6379> BRPOP list 0
1) "list"
2) "f"
127.0.0.1:6379> BRPOP noList 1
(nil)
(1.03s)

# 通过下标获得 key 列表中的某一个值
# LINDEX key index
127.0.0.1:6379> LINDEX list 2
"e"
127.0.0.1:6379> LINDEX list 6
(nil)

# 返回 key 列表的长度
# LLEN key
127.0.0.1:6379> LLEN list
(integer) 3

# 移除 key 列表中的某元素,指定个数,精确匹配
# LREM key count element
127.0.0.1:6379> LREM list 1 d
(integer) 1

# 截取 key 列表中指定范围的元素
# LTRIM key start stop
127.0.0.1:6379> LRANGE list 0 -1
1) "s"
2) "i"
3) "d"
4) "1"
5) "0"
6) "t"
127.0.0.1:6379> LTRIM list 3 5
OK
127.0.0.1:6379> LRANGE list 0 -1
1) "1"
2) "0"
3) "t"

# 移除 source 列表的最后一个元素,将该元素移动到 destination 列表去
# RPOPLPUSH source destination
127.0.0.1:6379> LRANGE list 0 -1
1) "1"
2) "0"
3) "t"
127.0.0.1:6379> RPOPLPUSH list l1
"t"
127.0.0.1:6379> LRANGE l1 0 -1
1) "t"

# 将 key 列表中的指定下标的值更新成新值
# LSET key index element
127.0.0.1:6379> EXISTS list
(integer) 0
127.0.0.1:6379> lset list 0 item    
(error) ERR no such key
127.0.0.1:6379> lpush list value1
(integer) 1
127.0.0.1:6379> LRANGE list 0 -1
1) "value1"
127.0.0.1:6379> lset list 0 item
OK
127.0.0.1:6379> LRANGE list 0 -1
1) "item"
127.0.0.1:6379> lset list 1 other
(error) ERR index out of range

# 将 value 插入到 key 列表中的某个元素前面或后面
# LINSERT key BEFORE|AFTER pivot element
127.0.0.1:6379> RPUSH mylist hello sid10t
(integer) 2
127.0.0.1:6379> LINSERT mylist before sid10t world
(integer) 3
127.0.0.1:6379> LRANGE mylist 0 -1
1) "hello"
2) "world"
3) "sid10t"

 

应用场景

消息队列

消息队列在存取消息时,必须要满足三个需求,分别是消息保序、处理重复的消息和保证消息可靠性

Redis 的 ListStream 两种数据类型,就可以满足消息队列的这三个需求。我们先来了解下基于 List 的消息队列实现方法,在之后的博文中再细说 Stream

1、如何满足消息保序需求?

List 本身就是按先进先出的顺序对数据进行存取的,所以,如果使用 List 作为消息队列保存消息的话,就已经能满足消息保序的需求了。

List 可以使用 LPUSH + RPOP 或者 RPUSH+LPOP 命令实现消息队列。

  • 生产者使用 LPUSH key value[value...] 将消息插入到队列的头部,如果 key 不存在则会创建一个空的队列再插入消息。
  • 消费者使用 RPOP key 依次读取队列的消息,先进先出。

不过,在消费者读取数据时,有一个潜在的性能风险点。

在生产者往 List 中写入数据时,List 并不会主动地通知消费者有新消息写入,如果消费者想要及时处理消息,就需要在程序中不停地调用 RPOP 命令(比如使用一个 while True 循环)。如果有新消息写入,RPOP 命令就会返回结果,否则,RPOP 命令返回空值,再继续循环。

所以,即使没有新消息写入 List,消费者也要不停地调用 RPOP 命令,这就会导致消费者程序的 CPU 一直消耗在执行 RPOP 命令上,带来不必要的性能损失。

为了解决这个问题,Redis提供了 BRPOP 命令。BRPOP 命令也称为阻塞式读取,客户端在没有读到队列数据时,自动阻塞,直到有新的数据写入队列,再开始读取新数据。和消费者程序自己不停地调用 RPOP 命令相比,这种方式能节省 CPU 开销。

2、如何处理重复的消息?

消费者要实现重复消息的判断,需要 2 个方面的要求:

  • 每个消息都有一个全局的 ID。
  • 消费者要记录已经处理过的消息的 ID。当收到一条消息后,消费者程序就可以对比收到的消息 ID 和记录的已处理过的消息 ID,来判断当前收到的消息有没有经过处理。如果已经处理过,那么,消费者程序就不再进行处理了。

但是 List 并不会为每个消息生成 ID 号,所以我们需要自行为每个消息生成一个全局唯一 ID,生成之后,我们在用 LPUSH 命令把消息插入 List 时,需要在消息中包含这个全局唯一 ID。

例如,我们执行以下命令,就把一条全局 ID 为 111000102、库存量为 99 的消息插入了消息队列:

127.0.0.1:6379> LPUSH mq "111000102:stock:99"
(integer) 1

3、如何保证消息可靠性?

当消费者程序从 List 中读取一条消息后,List 就不会再留存这条消息了。所以,如果消费者程序在处理消息的过程出现了故障或宕机,就会导致消息没有处理完成,那么,消费者程序再次启动后,就没法再次从 List 中读取消息了。

为了留存消息,List 类型提供了 BRPOPLPUSH 命令,这个命令的作用是让消费者程序从一个 List 中读取消息,同时,Redis 会把这个消息再插入到另一个 List(可以叫作备份 List)留存

这样一来,如果消费者程序读了消息但没能正常处理,等它重启后,就可以从备份 List 中重新读取消息并进行处理了。

好了,到这里可以知道基于 List 类型的消息队列,满足消息队列的三大需求(消息保序、处理重复的消息和保证消息可靠性)。

  • 消息保序:使用 LPUSH + RPOP
  • 阻塞读取:使用 BRPOP
  • 重复消息处理:生产者自行实现全局唯一 ID;
  • 消息的可靠性:使用 BRPOPLPUSH
List 作为消息队列有什么缺陷?

List 不支持多个消费者消费同一条消息,因为一旦消费者拉取一条消息后,这条消息就从 List 中删除了,无法被其它消费者再次消费。

要实现一条消息可以被多个消费者消费,那么就要将多个消费者组成一个消费组,使得多个消费者可以消费同一条消息,但是 List 类型并不支持消费组的实现

这就要说起 Redis 从 5.0 版本开始提供的 Stream 数据类型了,Stream 同样能够满足消息队列的三大需求,而且它还支持「消费组」形式的消息读取。
 

后记

Redis 五大常见数据类型之一的 List 就先讲到这里了,后续还会有其他类型的讲解呢,敬请关注!

参考资料:

📝 上篇精讲: 【Redis】五大常见的数据类型之 Hash
💖 我是  𝓼𝓲𝓭𝓲𝓸𝓽,期待你的关注;
👍 创作不易,请多多支持;
🔥 系列专栏: Redis
目录
相关文章
|
4月前
|
存储 消息中间件 NoSQL
【Redis】常用数据结构之List篇:从常用命令到典型使用场景
本文将系统探讨 Redis List 的核心特性、完整命令体系、底层存储实现以及典型实践场景,为读者构建从理论到应用的完整认知框架,助力开发者在实际业务中高效运用这一数据结构解决问题。
|
6月前
|
存储 NoSQL 定位技术
Redis数据类型面试给分情况
Redis常见数据类型包括:string、hash、list、set、zset(有序集合)。此外还包含高级结构如bitmap、hyperloglog、geo。不同场景可选用合适类型,如库存用string,对象存hash,列表用list,去重场景用set,排行用zset,签到用bitmap,统计访问量用hyperloglog,地理位置用geo。
164 5
|
6月前
|
NoSQL Java Redis
Redis基本数据类型及Spring Data Redis应用
Redis 是开源高性能键值对数据库,支持 String、Hash、List、Set、Sorted Set 等数据结构,适用于缓存、消息队列、排行榜等场景。具备高性能、原子操作及丰富功能,是分布式系统核心组件。
636 2
|
存储 缓存 NoSQL
解决Redis缓存数据类型丢失问题
解决Redis缓存数据类型丢失问题
516 85
|
10月前
|
NoSQL Redis
Redis的常用数据类型有哪些 ?
Redis 有 5 种基础数据结构,它们分别是:string(字符串)、list(列表)、hash(字典)、set(集 合) 和 zset(有序集合)
|
存储 NoSQL Redis
redis常见数据类型
Redis 是一种基于内存的键值存储数据库,支持字符串、哈希表、列表、集合及有序集合等多种数据类型,每种类型均有特定用途与适用场景,提供丰富的命令操作,适用于高速数据访问与处理。
237 5
|
存储 消息中间件 NoSQL
使用Java操作Redis数据类型的详解指南
通过使用Jedis库,可以在Java中方便地操作Redis的各种数据类型。本文详细介绍了字符串、哈希、列表、集合和有序集合的基本操作及其对应的Java实现。这些示例展示了如何使用Java与Redis进行交互,为开发高效的Redis客户端应用程序提供了基础。希望本文的指南能帮助您更好地理解和使用Redis,提升应用程序的性能和可靠性。
300 1
|
存储 消息中间件 NoSQL
Redis数据结构:List类型全面解析
Redis数据结构——List类型全面解析:存储多个有序的字符串,列表中每个字符串成为元素 Eelement,最多可以存储 2^32-1 个元素。可对列表两端插入(push)和弹出(pop)、获取指定范围的元素列表等,常见命令。 底层数据结构:3.2版本之前,底层采用**压缩链表ZipList**和**双向链表LinkedList**;3.2版本之后,底层数据结构为**快速链表QuickList** 列表是一种比较灵活的数据结构,可以充当栈、队列、阻塞队列,在实际开发中有很多应用场景。
|
消息中间件 NoSQL Redis
Redis---List数据类型操作
一、概述: 在Redis中,List类型是按照插入顺序排序的字符串链表。和数据结构中的普通链表一样,我们可以在其头部(left)和尾部(right)添加新的元素。在插入时,如果该键并不存在,Redis将为该键创建一个新的链表。与此相反,如果链表中所有的元素均被移除,那么该键也将会被从数据库中删除。List中可以包含的最大元素数量是4294967295。 从元素插入和删除的
1932 0
|
8月前
|
缓存 NoSQL 关系型数据库
美团面试:MySQL有1000w数据,redis只存20w的数据,如何做 缓存 设计?
美团面试:MySQL有1000w数据,redis只存20w的数据,如何做 缓存 设计?
美团面试:MySQL有1000w数据,redis只存20w的数据,如何做 缓存 设计?