• SpringBoot集成Redis业务功能 02、定时任务+Redis删除特定前缀key的优雅实现


    前言

    所有博客文件目录索引:博客目录索引(持续更新)


    业务背景

    在此实现了玩家积分的每日重置,此时又出现一个需求,也就是每天晚上12点,重置所有玩家可以重置积分的次数,比如说今天晚上11点用完了3次重置积分,那么过了晚上12点又可以重置积分了。

    对于每日玩家重置积分次数方案见:SpringBoot集成Redis业务功能 01、限制玩家积分的每日重置次数


    方案思路

    之前重置玩家每日积分3次是采用redis来实现的,每位玩家在进行重置积分操作时,就会进行存储reids的键值对,key时拼接玩家uid的字符串,value则是每日次数,在set时是设置24小时也就是1天过期。

    基于此方案上,我想了想那么就使用定时任务+批量删除redis前缀key来实现比较合适。

    其中的批量删除redis的前缀key有两种方案实现

    1. 直接使用keys命令来进行匹配所有的key,然后进行批量delete删除(不推荐)。【问题:数据量达到几百万,keys这个指令就会导致 Redis 服务卡顿,因为 Redis 是单线程程序,顺序执行所有指令,其它指令必须等到当前的 keys 指令执行完了才可以继续 。】
    2. 使用scan渐进式遍历获取到所有的key,实现keys的效果,需要进行多次scan**(推荐)**。【好处:采用渐进式遍历的方式来解决keys命令可能带来的阻塞问题,每次scan命令的时间复杂度是 O(1) ,但是要真正实现keys的功能,需要执行多次scan。】
    3. redis+lua脚本(暂未使用)。

    当前实现方案中就是使用的scan渐进式遍历来进行获取。


    实现方案

    批量删除redis的key

    关于SpringBoot如何集成Redis见:SpringBoot整合篇 04、Springboot整合Redis

    在下面代码中我们使用到了MultiKeyCommands,这个类是jedis里的,那么我们就需要将redis启动器中的lettuce替换为jedis,如下来进行排除依赖并添加jedis依赖:

    
    <dependency>
        <groupId>org.springframework.bootgroupId>
        <artifactId>spring-boot-starter-data-redisartifactId>
        <exclusions>
            <exclusion>
                <groupId>io.lettucegroupId>
                <artifactId>lettuce-coreartifactId>
            exclusion>
        exclusions>
    dependency>
    
    <dependency>
        <groupId>redis.clientsgroupId>
        <artifactId>jedisartifactId>
        <scope>compilescope>
    dependency>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    下面scan方法已经封装好了,使用方式:调用方法,直接传入要匹配的前缀key即可使用scan返回所有匹配到的key。

    • 一轮是获取1000个的。
    /**
     * redis工具类
     */
    @SuppressWarnings(value = { "unchecked", "rawtypes" })
    @Component
    public class RedisCache {
       /**
         *
         * @param key 要匹配的key前缀
         * @return 匹配到的批量key值
         */
        public Set<String> scan(String key) {
            return (Set<String>) redisTemplate.execute((RedisCallback<Set<String>>) connection -> {
                HashSet<String> keys = new HashSet<>();
                JedisCommands commands = (JedisCommands) connection.getNativeConnection();
                MultiKeyCommands multiKeyCommands = (MultiKeyCommands) commands;
                //组装scan请求参数(匹配内容+请求数量)
                ScanParams scanParams = new ScanParams();
                scanParams.match("*" + key + "*");
                scanParams.count(1000);
                //执行scan命令(批量去获取)
                ScanResult<String> scan = multiKeyCommands.scan("0", scanParams);
                while (scan.getCursor() != null) {
                    keys.addAll(scan.getResult());
                    if ("0".equals(scan.getCursor())) {
                        break;
                    }
                    scan = multiKeyCommands.scan(scan.getCursor(), scanParams);
                }
                return keys;
            });
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33

    定时任务

    定时任务使用Spring给我们提供的,按照如下进行配置即可:

    1、在启动器上开启定时任务注解

    @EnableScheduling //开启定时任务
    
    • 1

    2、注册Bean对象,在指定的方法上添加定时任务注解

    对于定时任务的cron表示式可用在线工具生成:cron表达式生成工具

    • 生成的是7位,而@Scheduled中的cron要求是6位,我们只需要把最后一位表示年的去年即可。
    package com.chuangmeng.horserace.tasks;
    
    import com.chuangmeng.horserace.constant.RedisKeyConstant;
    import com.chuangmeng.horserace.utils.RedisCache;
    import lombok.extern.slf4j.Slf4j;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.scheduling.annotation.Scheduled;
    import org.springframework.stereotype.Component;
    import org.springframework.util.ObjectUtils;
    
    import java.util.Set;
    
    @Component
    @Slf4j
    public class PlayerTask {
    
        @Autowired
        private RedisCache redisCache;
    
        /**
         * 每天晚上12点重置玩家积分
         */
        @Scheduled(cron = "0 0 0 * * ?")
        public void resetPlayerIntegral() {
            String key = "xxx";
            Set<String> keys = redisCache.scan(key);
            log.info("相关重置积分的key为:" + keys);
            if (!ObjectUtils.isEmpty(keys)) {
                redisCache.deleteObject(keys);
                log.info("重置玩家积分完成!");
            }
        }
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34

    完成重置操作也很简单,就是调用下scan()方法,然后拿到所有的key,执行删除命令就好。


    测试

    使用redis手动来去进行匹配前缀key:

    # 方式一:使用keys来进行匹配
    keys "chuangmeng:horserace:resetimtegral:*"
    
    # 方式二:使用scan来进行渐进式获取,第一次设置游标为0,会响应过来两个数据,一个是下一次你要进行遍历开始的游标,以及一组匹配的数据。若是下一组游标为0,那么就无需进行scan获取了,已经获取了全部
    scan 0 match "chuangmeng:horserace:resetimtegral:*"
    scan 17 match "chuangmeng:horserace:resetimtegral:*"
    
    # 删除key
    del chuangmeng:horserace:resetimtegral:3334322232  chuangmeng:horserace:resetimtegral:12364322232
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    对于程序的话我们测试可以设置个1分钟定时任务或者直接在test中调用方法测试即可!


    参考资料

    [1]. Spring Data Redis 是如何在 Jedis 和 Lettuce 之间切换的?springboot2 切换jedis

    [2]. 在RedisTemplate中使用scan代替keys指令

    [3]. SpringBoot整合Redis,缓存批量删除 | redisTemplate.keys(pattern)模糊查询找不到keys,“ * “ 通配符无效

    我是长路,感谢你的耐心阅读。如有问题请指出,我会积极采纳!
    欢迎关注我的公众号【长路Java】,分享Java学习文章及相关资料
    Q群:851968786 我们可以一起探讨学习
    注明:转载可,需要附带上文章链接

  • 相关阅读:
    网络抓包技术: scapy
    职场生涯亮红灯要注意这些
    2024 王道考研-数据结构(线性表_2)
    2024年科技前瞻:AI辅助研发引领未来创新浪潮
    js对象的声明及使用
    NetHTTPClient访问https网站出错。
    《吐血整理》进阶系列教程-拿捏Fiddler抓包教程(14)-Fiddler断点(breakpoints)实战,篡改或伪造数据
    xctf攻防世界 MISC薪手进阶区
    数据库(mysql)之用户管理
    【算法练习Day49】每日温度&&下一个更大元素 I
  • 原文地址:https://blog.csdn.net/cl939974883/article/details/126154674