【Redis技术专区】「实战案例」谈谈使用Redis缓存时高效的批量删除的几种方案

本文涉及的产品
云数据库 Redis 版,标准版 2GB
推荐场景:
搭建游戏排行榜
云原生内存数据库 Tair,内存型 2GB
简介: 【Redis技术专区】「实战案例」谈谈使用Redis缓存时高效的批量删除的几种方案

前因后果

之前我们的服务,在上线的时候发现有一些大Key的使用不是很规范,特别是没有设置过期时间,因此导致redis中内存的数据越来越多,目前Redis节点的内存已经快撑不住了。所以根据缓存键的规则去批量删除这些数据,比较常见的就是按前缀去删除。

现在由于不得以为的原因要删除这几百个Key-Value的数据,这个时候我们肯定就要把缓存键全部删除掉。一般情况下在Redis中是可以很容易去实现的。但是如果在不阻塞业务的前提下,并且以高效的方式进行清理内存数据。就需要好好想想办法了。

批量删除redis数据方法

利用的是Linux的xargs命令

我们可以通过redis-cli的模式,进行访问之后登录到了Redis-Server服务,由于是必须要使用Linux的xargs命令,所以必须要连带指令在Linux环境,而不能提前通过redis-cli进行登录到redis-server服务。否则会报错说xargs无效。

css

复制代码

redis-cli -h [ip] -p [port ]  -a  [password]  keys "prefix*" | xargs redis-cli -h 127.0.0.1 -p 6379 -a '123' del

上面的指令主要由三部分连接组成:

  • redis-cli -h [ip] -p [port ] -a [password]:主要需要用于登录到redis-cli的只处理操作。
  • keys "prefix*":随后主要是通过redis-cli的命令进行 keys指令进行匹配某前缀相关的数据集合。
  • | xargs redis-cli -h [ip] -p [port ] -a [password] del:主要是通过管道符进行连接,之后再进行连接redis-server服务,之后进行将之前的参数传入到xargs之后,作为del的参数进行执行删除操作。

xargs指令

xargs:是一条Unix和类Unix操作系统的常用命令;它的作用是将参数列表转换成小块分段传递给其他命令,以避免参数列表过长的问题。可单独使用,也可使用管道符、重定位符等与其他命令配合使用。

css

复制代码

xargs [ -p ] [ -t] [ -e[ EOFString ] ] [ -EEOFString ] [ -i[ ReplaceString ] ] [ -IReplaceString ] [ -l [ Number ] ] [ -L Number ] [ -n Number [ -x ] ] [ -s Size ] [ Command [ Argument ... ] ]
命令格式

xargs:一般是和管道一起使用。

bash

复制代码

somecommand |xargs -item  command
参数:
  • -a file 从文件中读入作为 stdin
  • -e flag ,注意有的时候可能会是-E,flag必须是一个以空格分隔的标志,当xargs分析到含有flag这个标志的时候就停止。
  • -p 当每次执行一个argument的时候询问一次用户。
  • -n num 后面加次数,表示命令在执行的时候一次用的argument的个数,默认是用所有的。
  • -t 表示先打印命令,然后再执行。
  • -i 或者是-I,这得看linux支持了,将xargs的每项名称,一般是一行一行赋值给 {},可以用 {} 代替。
  • -r no-run-if-empty 当xargs的输入为空的时候则停止xargs,不用再去执行了。
  • -s num 命令行的最大字符数,指的是 xargs 后面那个命令的最大命令行字符数。
  • -L num 从标准输入一次读取 num 行送给 command 命令。
  • -l 同 -L。
  • -d delim 分隔符,默认的xargs分隔符是回车,argument的分隔符是空格,这里修改的是xargs的分隔符。
  • -x exit的意思,主要是配合-s使用。。
  • -P 修改最大的进程数,默认是1,为0时候为as many as it can ,这个例子我没有想到,应该平时都用不到的吧。

使用Lua脚本删除百万/千万级的key

如果以上xargs方法删除不了的,或者执行xargs命令报错的。那么可以使用lua脚本,redis有内置的lua解释器。在lua脚本中使用scan扫描key,并依次删除,当删除数量达到1万时,脚本直接返回,完成本次调用,如果删除的key数量大于0,就循环调用脚本进行删除。

vbnet

复制代码

Warning: a NUL character occurred in the input.  It cannot be passed through in the argument list.  Did you mean to use the --null option?

Lua脚本是什么?

Lua是一种轻量小巧的脚本语言,用标准C语言编写并以源代码形式开放, 其设计目的是为了嵌入应用程序中,从而为应用程序提供灵活的扩展和定制功能。其设计目的是为了嵌入应用程序中,从而为应用程序提供灵活的扩展和定制功能。

Lua脚本的指令格式

有兴趣的小伙伴,可以参考:redis.cn/commands/ev…

css

复制代码

EVAL script numkeys key [key …] arg [arg …]
  • script:待执行的脚本文件
  • numkeys:key的个数
Lua脚本执行参数
  • [key …]:对应的key,可以是一个,可以是多个
  • [arg …]:与key对应的值,可以是一个,可以是多个
Lua获取传参数据

Lua的下表索引是从1开始的,key的获取方式,KEYS[下标索引],如KEYS[1],取第一个值,值的获取,ARGV[1]

示例

css

复制代码

eval “return {KEYS[1],KEYS[2],ARGV[1],ARGV[2]}” 2 key1 key2 first second
Lua脚本的案例(keys)
  1. 获取传入的需要批量删除的key的前缀
  2. 记住 lua的下标索引是从1开始 不是0 不是0 不是0

lua

复制代码

local key = KEYS[1]
if( key ~= nil) then
        --这里通过keys查询出所有符合条件的数据
        local dataList = redis.call('keys',KEYS[1])
        --判断是否找到数据
        if(dataList ~= nil) then
                --循环删除
                for i=1,#dataList,1 do
                        redis.call('del',dataList[i])
                end
                --返回删除的行数
                return #dataList
        else
                return 0
        end
else
        return 0
end

推荐使用scan获取数据删除,我们知道redis是一个单线程的,当我们库里面存在大量数据的时候,使用keys * 的方式匹配数据的时候,可能需要好几秒才能处理完,那么在这个几秒的时间里是处于线程阻塞的,其他所有的redis操作都是处于等待状态,这样对系统的可用性是有影响的,因此,这里使用scan的方式匹配数据。

scan介绍

css

复制代码

SCAN cursor [MATCH pattern] [COUNT count]

SCAN 命令是一个基于游标的迭代器(cursor based iterator): SCAN 命令每次被调用之后, 都会向用户返回一个新的游标, 用户在下次迭代时需要使用这个新游标作为 SCAN 命 令的游标参数, 以此来延续之前的迭代过程。

Lua脚本的案例(scan)

lua

复制代码

local limitSize = tonumber(ARGV[1]) -- 最多删除多少个key
local batchSize = limitSize -- scan一次最多扫描多少个key
if (batchSize > 10000) then -- 一次扫描不能超过1w条
    batchSize = 10000
end
local function scan(key)
    local cursor = 0
    local keynum = 0
    repeat
        local res = redis.call("scan", cursor, "match", key, 'COUNT', batchSize)
        if (res ~= nil and #res >= 0) then
            redis.replicate_commands()
            cursor = tonumber(res[1])
            local ks = res[2]
            local size = #ks
            for i=1,size,1 do
                redis.call("del", tostring(ks[i]))
                keynum = keynum + 1
                if (keynum >= limitSize) then -- 已经删除了指定数量的key, 返回
                  return keynum
                end
            end
        end
    until (cursor <= 0)
    return keynum
end
local total = scan(KEYS[1])
return total

当 SCAN 命令的游标参数被设置为 0 时, 服务器将开始一次新的迭代, 而当服务器向用户返回值为 0 的游标时, 表示迭代已结束。

通俗点理解就是,基于游标的迭代器redis会慢慢一次次的将数据返回回来,从而防止线程阻塞。

此外还有一个小贴士就是可以使用UNLINK删除,区别于del的是这个是异步执行的,这条指令要版本大于4.0.0 小于4.0.0就使用del

vbnet

复制代码

redis.call("UNLINK",key)


相关实践学习
基于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
相关文章
|
5天前
|
缓存 安全 Android开发
Android经典实战之用Kotlin泛型实现键值对缓存
本文介绍了Kotlin中泛型的基础知识与实际应用。泛型能提升代码的重用性、类型安全及可读性。文中详细解释了泛型的基本语法、泛型函数、泛型约束以及协变和逆变的概念,并通过一个数据缓存系统的实例展示了泛型的强大功能。
14 2
|
13天前
|
SQL 存储 NoSQL
Redis6入门到实战------ 一、NoSQL数据库简介
这篇文章是关于NoSQL数据库的简介,讨论了技术发展、NoSQL数据库的概念、适用场景、不适用场景,以及常见的非关系型数据库。文章还提到了Web1.0到Web2.0时代的技术演进,以及解决CPU、内存和IO压力的方法,并对比了行式存储和列式存储数据库的特点。
Redis6入门到实战------ 一、NoSQL数据库简介
|
13天前
|
NoSQL 算法 安全
Redis6入门到实战------ 四、Redis配置文件介绍
这篇文章详细介绍了Redis配置文件中的各种设置,包括单位定义、包含配置、网络配置、守护进程设置、日志记录、密码安全、客户端连接限制以及内存使用策略等。
Redis6入门到实战------ 四、Redis配置文件介绍
|
13天前
|
NoSQL Redis 数据安全/隐私保护
Redis6入门到实战------ 二、Redis安装
这篇文章详细介绍了Redis 6的安装过程,包括下载、解压、编译、安装、配置以及启动Redis服务器的步骤。还涵盖了如何设置Redis以在后台运行,如何为Redis设置密码保护,以及如何配置Redis服务以实现开机自启动。
Redis6入门到实战------ 二、Redis安装
|
13天前
|
NoSQL Java Redis
Redis6入门到实战------思维导图+章节目录
这篇文章提供了Redis 6从入门到实战的全面学习资料,包括思维导图和各章节目录,涵盖了NoSQL数据库、Redis安装配置、数据类型、事务、持久化、主从复制、集群等核心知识点。
Redis6入门到实战------思维导图+章节目录
|
12天前
|
NoSQL 安全 Java
Redis6入门到实战------ 三、常用五大数据类型(字符串 String)
这篇文章深入探讨了Redis中的String数据类型,包括键操作的命令、String类型的命令使用,以及String在Redis中的内部数据结构实现。
Redis6入门到实战------ 三、常用五大数据类型(字符串 String)
|
12天前
|
NoSQL 关系型数据库 Redis
Redis6入门到实战------ 九、10. Redis_事务_锁机制_秒杀
这篇文章深入探讨了Redis事务的概念、命令使用、错误处理机制以及乐观锁和悲观锁的应用,并通过WATCH/UNWATCH命令展示了事务中的锁机制。
Redis6入门到实战------ 九、10. Redis_事务_锁机制_秒杀
|
12天前
|
NoSQL Java Redis
Redis6入门到实战------ 八、Redis与Spring Boot整合
这篇文章详细介绍了如何在Spring Boot项目中整合Redis,包括在`pom.xml`中添加依赖、配置`application.properties`文件、创建配置类以及编写测试类来验证Redis的连接和基本操作。
Redis6入门到实战------ 八、Redis与Spring Boot整合
|
1天前
|
运维 监控 NoSQL
【Redis】哨兵(Sentinel)原理与实战全解~炒鸡简单啊
Redis 的哨兵模式(Sentinel)是一种用于实现高可用性的机制。它通过监控主节点和从节点,并在主节点故障时自动进行切换,确保集群持续提供服务。哨兵模式包括主节点、从节点和哨兵实例,具备监控、通知、自动故障转移等功能,能显著提高系统的稳定性和可靠性。本文详细介绍了哨兵模式的组成、功能、工作机制以及其优势和局限性,并提供了单实例的安装和配置步骤,包括系统优化、安装、配置、启停管理和性能监控等。此外,还介绍了如何配置主从复制和哨兵,确保在故障时能够自动切换并恢复服务。
|
24天前
|
缓存 NoSQL Java
SpringBoot的三种缓存技术(Spring Cache、Layering Cache 框架、Alibaba JetCache 框架)
Spring Cache 是 Spring 提供的简易缓存方案,支持本地与 Redis 缓存。通过添加 `spring-boot-starter-data-redis` 和 `spring-boot-starter-cache` 依赖,并使用 `@EnableCaching` 开启缓存功能。JetCache 由阿里开源,功能更丰富,支持多级缓存和异步 API,通过引入 `jetcache-starter-redis` 依赖并配置 YAML 文件启用。Layering Cache 则提供分层缓存机制,需引入 `layering-cache-starter` 依赖并使用特定注解实现缓存逻辑。
127 1
SpringBoot的三种缓存技术(Spring Cache、Layering Cache 框架、Alibaba JetCache 框架)
下一篇
云函数