龙空技术网

Redis实战(9)-SortedSet之再谈游戏充值排行榜(处理历史充值记录)

程序员实战基地 313

前言:

现在小伙伴们对“mysql执行sql语句游戏充值”可能比较关心,朋友们都需要分析一些“mysql执行sql语句游戏充值”的相关内容。那么小编在网上搜集了一些对于“mysql执行sql语句游戏充值””的相关知识,希望你们能喜欢,你们快快来学习一下吧!

摘要

每当我们谈起缓存中间件Redis的应用场景时,我们一般都会根据其数据结构联想到对应的应用场景,有序集合SortedSet也不例外,"排行榜"一直都是与其紧密挂钩、不得不谈的其中一种实战场景!本文我们将继续再谈"游戏充值排行榜",介绍如何去处理历史已经存在的充值记录 或者 在将充值记录塞入缓存Cache失败时如何开启后续的补偿处理措施!

内容

在上篇文章中,我们已经给各位小伙伴介绍了如何基于Spring Boot2.0 + 缓存Redis的SortedSet以实际的代码实战一种典型的业务场景"游戏充值排行榜",在文中我们介绍了这一业务场景两大典型的核心功能模块,即"用户充值"、"获取充值排行榜",各位小伙伴可以自行前往回顾!

然而,这世间本就没有十全十美之物,"游戏充值排行榜"这一业务场景也不例外,虽然我们基本上已经实现了该业务场景几乎所有的功能模块,但是我们却忽略了其他两种情况:

A.如果"充值排行榜"这一功能模块是增量式的需求,那么上线时如何去处理历史的用户充值记录呢?你总不能说我们的"充值排行榜"对于以往充值的用户记录不生效吧?(那样岂不令人笑掉大牙!)

B.虽然我们的代码看似完美,但是要知道Bug是无处不在的,这些Bug有的是能一眼被洞穿的,也有的是后知后觉的,"用户充值的过程"便是如此,如果用户充值后插入数据库DB成功、但是插入缓存Cache失败(DB事务不回滚的前提),那毫无疑问,最终得出来的"充值排行榜"一定是不准确的(因为我们是直接从缓存Redis中获取的)!

带着这两大问题,我们给大家提供了一种并非十全十美的,但是却能保证"最终一致性"的充值排行榜的解决方案,那就是万能的定时任务调度!

既然是定时任务调度,那么这个定时任务是做啥的呢?没错,它要完成的任务就是开启一个定时时钟,基于数据库DB中的"用户充值记录表",借助数据库提供的Order By、Group By等查询得出目前为止所有有效用户的"充值排行榜",下面我们以实际的代码进行实战。


(1)直接建立一个定时任务调度类PhoneFareScheduler,并开发相应的方法实现具体的定时任务逻辑,其完整源代码如下所示:

/**补偿机制:手机号码充值排行榜 * @Author:debug (SteadyJack) * @Link: weixin-> debug0868 qq-> 1948831260**/@Componentpublic class PhoneFareScheduler {    private static final Logger log= LoggerFactory.getLogger(PhoneFareScheduler.class);    @Autowired    private PhoneFareMapper phoneFareMapper;    @Autowired    private RedisTemplate redisTemplate;    //时间频度设定为30min,当然啦,具体的设定要根据实际情况而定    @Scheduled(cron = "0 0/30 * * * ?")    public void sortFareScheduler(){        log.info("--补偿性手机号码充值排行榜-定时任务");        this.cacheSortResult();    }    @Async("threadPoolTaskExecutor")    private void cacheSortResult(){        try {            ZSetOperations<String,FareDto> zSetOperations=redisTemplate.opsForZSet();            List<PhoneFare> list=phoneFareMapper.getAllSortFares();            if (list!=null && !list.isEmpty()){                redisTemplate.delete(Constant.RedisSortedSetKey2);                list.forEach(fare -> {                    FareDto dto=new FareDto(fare.getPhone());                    zSetOperations.add(Constant.RedisSortedSetKey2,dto,fare.getFare().doubleValue());                });            }        }catch (Exception e){            log.error("--补偿性手机号码充值排行榜-定时任务-发生异常:",e.fillInStackTrace());        }    }}

值得一提的是,在该定时任务调度中我们设定的时间频率为 每30min进行执行一次任务,实现"充值排行榜"的大洗盘!也就是说,如果前端"排行榜"页面数据出现差错,那么其恢复正确的等待时间是30min(因为我们的定时任务就是前往数据库DB,查询获取得到排行榜,当然啦,其前提是保证DB中的数据是正确无误的!)


(2)其中,phoneFareMapper.getAllSortFares() 的作用就是前往数据库Mysql,通过Group By、Order By和SUM等查询得到排行榜,其完整的动态SQL如下所示:

<!--基于数据库的补偿排名机制-->  <select id="getAllSortFares" resultType="com.boot.debug.redis.model.entity.PhoneFare">    SELECT        phone,        SUM(fare) AS fare    FROM        phone_fare    GROUP BY        phone    ORDER BY        fare DESC  </select>

除此之外,@Async("threadPoolTaskExecutor") 的作用便是采用"线程池-多线程的方式异步执行定时任务",故而我们需要作一个全局的Config,用于配置线程池-多线程的相关信息:

/**线程池-多线程配置 * @Author:debug (SteadyJack) * @Link: weixin-> debug0868 qq-> 1948831260**/public class ThreadConfig {    @Bean("threadPoolTaskExecutor")    public Executor threadPoolTaskExecutor(){        ThreadPoolTaskExecutor executor=new ThreadPoolTaskExecutor();        executor.setCorePoolSize(4);        executor.setMaxPoolSize(8);        executor.setKeepAliveSeconds(10);        executor.setQueueCapacity(8);        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());        return executor;    }}

至此,我们已经撸完了"游戏充值排行榜"这一完整业务的"补偿机制"功能代码了,在测试之前,我们先"偷偷"在数据库表phone_fare中新增几条充值记录,代表"以前存在的历史充值记录"或者"插入DB成功,但插入缓存失败的充值记录",如下图所示:


最后我们基于Postman测试一波吧,下面一张图足以说明一切了:

好了,本篇文章我们就介绍到这里了,建议各位小伙伴一定要照着文章提供的样例代码撸一撸,只有撸过才能知道这玩意是咋用的,否则就成了"空谈者"!

对Redis相关技术栈以及实际应用场景实战感兴趣的小伙伴可以前往Debug搭建的技术社区的课程中心进行学习观看:!

其他相关的技术,感兴趣的小伙伴可以私信Debug!

标签: #mysql执行sql语句游戏充值