Redis - Redis List 功能详解与工业应用

本文涉及的产品
云数据库 Redis 版,社区版 2GB
推荐场景:
搭建游戏排行榜
简介: Redis 列表 List 是简单的字符串列表,按照插入顺序排序,一个列表最多可以包含 232- 1 个元素 (4294967295, 每个列表超过40亿个元素)。下面介绍下 Redis List 常用功能以及在工业场景下 Redis List 的几种使用场景。......

一.引言

Redis 列表 List 是简单的字符串列表,按照插入顺序排序,一个列表最多可以包含 232 - 1 个元素 (4294967295, 每个列表超过40亿个元素)。下面介绍下 Redis List 常用功能以及在工业场景下 Redis List 的几种使用场景。

二.常见功能

博主使用 Scala + Jedis 的组合进行示例演示,首先初始化 Jedis 与 list key:

val redis = new Jedis(host, port)
    val key = "testRedisList"

image.gif

1.lpush

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

执行 lpush 插入三个元素:

redis.lpush("testRedisList", "A")
    redis.lpush("testRedisList", "B")
    redis.lpush("testRedisList", "C")

image.gif

[C, B, A]

image.gif

也可以一次性插入三个元素:

redis.lpush("testRedisList", "A", "B", "C")

image.gif

[C, B, A, C, B, A]

image.gif

注意 lpush 是插入到列表头部,所以最后插入的 C 在列表最前面。经过两轮 lpush,列表的长度已经到达 6。

2.lrange

Redis Lrange 返回列表中指定区间内的元素,区间以偏移量 START 和 END 指定。 其中 0 表示列表的第一个元素, 1 表示列表的第二个元素,以此类推。 你也可以使用负数下标,以 -1 表示列表的最后一个元素, -2 表示列表的倒数第二个元素,以此类推。

遍历列表前6个元素:

println(redis.lrange("testRedisList", 0, 5))

image.gif

[C, B, A, C, B, A]

image.gif

也可以将 end 写多,只展示对应长度:

println(redis.lrange("testRedisList", 0, 10))

image.gif

[C, B, A, C, B, A]

image.gif

获取全部列表:

println(redis.lrange("testRedisList", 0, -1))

image.gif

3.rpush

Redis Rpush 命令用于将一个或多个值插入到列表的尾部(最右边)。如果列表不存在,一个空列表会被创建并执行 RPUSH 操作。 当列表存在但不是列表类型时,返回一个错误。与 lpush 执行反方向的插入。下面使用 rpush 在列表右侧插入3个元素:

redis.rpush("testRedisList", "A")
    redis.rpush("testRedisList", "B")
    redis.rpush("testRedisList", "C")

image.gif

[C, B, A, C, B, A, A, B, C]

image.gif

rpush 可以看做是 list.append,最终插入到列表末端,其次也可以像 lpush 一样一次插入多个元素。

redis.rpush("testRedisList", "A", "B", "C")

image.gif

[C, B, A, C, B, A, A, B, C, A, B, C]

image.gif

4.lpop

Redis Lpop 命令用于移除并返回列表的第一个元素。返回列表的第一个元素,如果列表不存在或列表为空则返回 null。

println(redis.lpop("testRedisList"))

image.gif

返回首字符 C,列表当前状态为:

[B, A, C, B, A, A, B, C, A, B, C]

image.gif

5.rpop

Redis Rpop 命令用于移除列表的最后一个元素,返回值为移除的元素。与上面 lpop 类似,只是元素位置不同。

println(redis.rpop("testRedisList"))

image.gif

返回列表最后一个元素 C,此时列表状态为:

[B, A, C, B, A, A, B, C, A, B]

image.gif

6.llen

Redis Llen 命令用于返回列表的长度。 如果列表 key 不存在,则 key 被解释为一个空列表,返回 0 。 如果 key 不是列表类型,返回一个错误。

println(redis.llen("testRedisList"))

image.gif

[B, A, C, B, A, A, B, C, A, B] 列表长度为10,如果 key 对应类型不匹配则报如下错误:

image.gif编辑

7.lindex

Redis Lindex 命令用于通过索引获取列表中的元素。你也可以使用负数下标,以 -1 表示列表的最后一个元素, -2 表示列表的倒数第二个元素,以此类推。如果下标超出数组界限也不会报错,而是返回 null。

println(redis.lindex("testRedisList", 0))
    println(redis.lindex("testRedisList", 11))

image.gif

数组长度为 10,0 索引为 B,11 则返回 null,不会报错。

8.linsert

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

redis.linsert("testRedisList", LIST_POSITION.BEFORE, "A", "D")

image.gif

在数组中 A 元素前添加 D 元素,[B, A, C, B, A, A, B, C, A, B] -> [B, D, A, C, B, A, A, B, C, A, B]。

LIST_POSITION 来自  redis.clients.jedis.BinaryClient.LIST_POSITION 类,redis 会从数组顺序搜索目标字符, ListPosition 分别为 AFTER 和 BEFORE,代表在目标字符后、前添加元素。

9.lrem

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

COUNT 的值可以是以下几种:

count > 0 : 从表头开始向表尾搜索,移除与 VALUE 相等的元素,数量为 COUNT 。

count < 0 : 从表尾开始向表头搜索,移除与 VALUE 相等的元素,数量为 COUNT 的绝对值。

count = 0 : 移除表中所有与 VALUE 相等的值。

redis.lrem("testRedisList", -2, "A")

image.gif

-2 A

- 代表 列表表尾开始搜索

2 代表删除2个元素

A 代表待删除的元素

执行上述命令后末尾的 2 个 A 被删除:

[B, D, A, C, B, A, A, B, C, A, B] -> [B, D, A, C, B, A, B, C, B]

10.lset

Redis Lset 通过索引来设置元素的值。当索引参数超出范围,或对一个空列表进行 LSET 时,返回一个错误。注意,该情况下越界会报错数组越界,而 lrange 越界则会截取数组部分,不会报错。

redis.lset("testRedisList", 0, "E")
    redis.lset("testRedisList", 12, "E")

image.gif

[B, D, A, C, B, A, B, C, B] 当前数组长度为 9,将 0 位,12 位设置为 E:

执行第一行命令得到:[E, D, A, C, B, A, B, C, B]

执行第二行命令得到:

image.gif编辑

11.ltrim

Redis Ltrim 对一个列表进行修剪(trim),不在指定区间之内的元素都将被删除。索引从 0 开始,可以使用负数代表末尾的数字。

redis.ltrim("testRedisList", 0, 5)

image.gif

保留列表中 6 个元素,注意这里的 start 和 end 都会保存,区间为左闭右闭形式。

[E, D, A, C, B, A, B, C, B] -> [E, D, A, C, B, A]

三.工业应用

1.状态监控

初始化空列表,使用 while(true) + time.sleep(period) 的形式定时监控列表的长度与内容,根据内容启动相关任务。

def monitorApplication(redis: Jedis, key: String, time: Int): Unit = {
    try {
      while (true) {
        val length = redis.llen(key)
        if (length > 0) {
          val value = redis.lpop(key)
          // 自定义
          doSomeThing(value)
        } else {
          Thread.sleep(time)
        }
      }
    }
  }

image.gif

2.队列消费 (数据量小)

多见于通过 redis 队列消费数据,数据生产方通过 rpush 不断向队列写入信息,消费方通过 while 判断队列长度,不断消费信息,常见于 Storm 自定义 Spout,Spark-Streaming 自定义 Receiver 以及 Flink 自定义 Source。

def loopConsumeRedisList(redis: Jedis, key: String): Unit = {
    // 循环消费数组
    try {
      while (redis.llen(key) > 0) {
        val value = redis.lpop(key)
        doSomeThing(value)
      }
    } catch {
      case e: JedisException => {
        e.printStackTrace()
      }
    }
  }

image.gif

3.队列消费 (数据量大)

当队列存储元素过大时,例如冗长的 Json,如果队列元素过多一次性 lrange(0, -1) 得到全部队列会造成 redis 流量堵塞与本地内存过大的问题,可以采用分治的方法批量获取 redis list 内容,getRangeList 方法负责根据 redisListLength 与规定步长生成对应范围数组,随后 foreach 分别处理每个小数组,最终 ltrim 将处理的元素清理掉。

def rangeConsumeRedisList(redis: Jedis, key: String): Unit = {
    val redisListLength = redis.llen(key)
    val limit = 100
    if (redisListLength < limit) {
      val allList = redis.lrange(key, 0, -1)
      println(allList)
    } else {
      val step = 50
      val range = genRangeList(redisListLength, step)
      // 批量处理
      range.foreach{ case (st, end) => {
        val tmpList = redis.lrange(key, st, end)
        println(tmpList)
      }}
      // 清除数据
      redis.ltrim(key, redisListLength + 1, -1)
    }
  }

image.gif

辅助函数 genRangeList:

def genRangeList(len: Long, step: Int): Array[(Long, Long)] = {
    val candidates = new ArrayBuffer[(Long, Long)]()
    // 异常值判断,输入长度小于步长
    if (len <= step) {
      candidates.append((0L, len))
      return candidates.toArray
    }
    // 循环添加范围
    var start = 0
    while (start < len) {
      candidates.append((start, start + step))
      start += step
    }
    candidates.toArray
  }

image.gif

例 - List 长度为 1000,每 100 个元素处理一次会生成如下 range,通过 lrange 分批处理即可:

genRangeList(1000, 100).foreach(println(_))

image.gif

image.gif编辑

这里分治的优化思路与 redis hashMap 的 hscan 有相同的思路,需要对 redis hashMap 采取分治的方法可以参考:Scala - Redis hgetAll 优化 by hscan

四.总结

redis list 的基本功能与工业场景大致就这些,其最基础的使用就是生产着 rpush,消费者 lpop 处理,一般不建议通过队列存储过大的元素,会对 redis IO 和 内存造成较大的压力。

相关实践学习
基于Redis实现在线游戏积分排行榜
本场景将介绍如何基于Redis数据库实现在线游戏中的游戏玩家积分排行榜功能。
云数据库 Redis 版使用教程
云数据库Redis版是兼容Redis协议标准的、提供持久化的内存数据库服务,基于高可靠双机热备架构及可无缝扩展的集群架构,满足高读写性能场景及容量需弹性变配的业务需求。 产品详情:https://www.aliyun.com/product/kvstore &nbsp; &nbsp; ------------------------------------------------------------------------- 阿里云数据库体验:数据库上云实战 开发者云会免费提供一台带自建MySQL的源数据库&nbsp;ECS 实例和一台目标数据库&nbsp;RDS实例。跟着指引,您可以一步步实现将ECS自建数据库迁移到目标数据库RDS。 点击下方链接,领取免费ECS&amp;RDS资源,30分钟完成数据库上云实战!https://developer.aliyun.com/adc/scenario/51eefbd1894e42f6bb9acacadd3f9121?spm=a2c6h.13788135.J_3257954370.9.4ba85f24utseFl
目录
相关文章
|
21天前
|
存储 消息中间件 NoSQL
Redis数据类型详解:选择合适的数据结构优化你的应用
Redis数据类型详解:选择合适的数据结构优化你的应用
|
1月前
|
存储 NoSQL Redis
【Redis】利用Redis List实现数据库分页快速查询
【Redis】利用Redis List实现数据库分页快速查询
97 0
|
21天前
|
存储 NoSQL 数据处理
Redis Lua脚本:赋予Redis更强大的逻辑与功能
Redis Lua脚本:赋予Redis更强大的逻辑与功能
|
26天前
|
缓存 NoSQL Shell
【Redis深度专题】「核心技术提升」探究Redis服务启动的过程机制的技术原理和流程分析的指南(持久化功能分析)
【Redis深度专题】「核心技术提升」探究Redis服务启动的过程机制的技术原理和流程分析的指南(持久化功能分析)
40 0
|
26天前
|
存储 缓存 NoSQL
【Redis深度专题】「核心技术提升」探究Redis服务启动的过程机制的技术原理和流程分析的指南(集群功能分析)(一)
【Redis深度专题】「核心技术提升」探究Redis服务启动的过程机制的技术原理和流程分析的指南(集群功能分析)
68 0
|
1月前
|
Cloud Native NoSQL 数据管理
Serverless 应用引擎常见问题之首次启动获取不到redis连接如何解决
Serverless 应用引擎(Serverless Application Engine, SAE)是一种完全托管的应用平台,它允许开发者无需管理服务器即可构建和部署应用。以下是Serverless 应用引擎使用过程中的一些常见问题及其答案的汇总:
28 3
Serverless 应用引擎常见问题之首次启动获取不到redis连接如何解决
|
1月前
|
缓存 NoSQL Java
spring cache整合redis实现springboot项目中的缓存功能
spring cache整合redis实现springboot项目中的缓存功能
46 1
|
1月前
|
NoSQL Redis 索引
[Redis]——Redis命令手册set、list、sortedset
[Redis]——Redis命令手册set、list、sortedset
|
1月前
|
存储 缓存 NoSQL
探索Redis的多样应用场景:加速和优化现代应用
探索Redis的多样应用场景:加速和优化现代应用
33 2
|
1月前
|
存储 消息中间件 NoSQL
Redis 常见数据类型(对象类型)和应用案列
接下来,让我们走进 Redis 的对象世界,Redis 5.0版本就已经支持了下面的 9 种类型,分别是 :字符串对象、列表对象、哈希对象、集合对象、有序集合对象、Bitmaps 对象、HyperLogLog 对象、Geospatial 对象、Stream对象。
Redis 常见数据类型(对象类型)和应用案列

热门文章

最新文章