Redis保证数据一致性-延时双删代码实现

Redis和数据库的数据一致性在某些场景下非常重要,如何最大程度保证Reids和数据库之间的数据一致呢?

想必大家第一时间会想到延时双删策略:

在修改数据库之前删除缓存,然后数据库中的数据修改完成后,再过一段时间再删一次缓存。

这篇文章就如何实现延时双删提供了案例代码,代码可能不是最优的,但是确实能够达到双删的效果。

具体代码实现:

RedisUtils.java

RedisUtils是一个自定义的接口,表示redis的工具类

package cn.edu.sgu.www.mhxysy.redis;import java.util.concurrent.TimeUnit;/*** redis工具类* @author heyunlin* @version 1.0*/
public interface RedisUtils {String get(String key);void set(String key, String value);void set(String key, String value, long timeout, TimeUnit timeUnit);void delete(String key);Long incrBy(String key);Boolean hasKey(String key);void expire(String key, long timeout, TimeUnit timeUnit);
}

StringRedisUtils.java

StringRedisUtils是基于StringRedisTemplate实现的操作Redis客户端的工具类,封装了一些常用的方法。

package cn.edu.sgu.www.mhxysy.redis;import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Component;import java.util.concurrent.TimeUnit;/*** 封装了StringRedisTemplate的redis工具类* @author heyunlin* @version 1.0*/
@Component
public class StringRedisUtils implements RedisUtils {private final StringRedisTemplate stringRedisTemplate;@Autowiredpublic StringRedisUtils(StringRedisTemplate stringRedisTemplate) {this.stringRedisTemplate = stringRedisTemplate;}/*** get key* @param key 键* @return String*/@Overridepublic String get(String key) {return getValueOperations().get(key);}/*** set key value* @param key 键* @param value 值*/@Overridepublic void set(String key, String value) {getValueOperations().set(key, value);}/*** set key value* expire key seconds* @param key 键* @param value 值* @param timeout 过期的时间* @param timeUnit 时间单位*/public void set(String key, String value, long timeout, TimeUnit timeUnit) {getValueOperations().set(key, value, timeout, timeUnit);}/*** del key* @param key 键*/@Overridepublic void delete(String key) {stringRedisTemplate.delete(key);}/*** key的值自增:incrby key* @param key 键* @return 自增后的值*/@Overridepublic Long incrBy(String key) {return getValueOperations().increment(key);}@Overridepublic Boolean hasKey(String key) {return stringRedisTemplate.hasKey(key);}/*** expire key seconds* @param key 键* @param timeout 过期的时间* @param timeUnit 时间单位*/@Overridepublic void expire(String key, long timeout, TimeUnit timeUnit) {stringRedisTemplate.expire(key, timeout, timeUnit);}/*** 获取ValueOperations对象* @return ValueOperations<String, String>*/private ValueOperations<String, String> getValueOperations() {return stringRedisTemplate.opsForValue();}}

RedisRepository.java

RoleAccountRepository是一个操作RoleAccount数据的redis仓库类,一个数据库实体类对应了一个XxxRepository,这些仓库类实现同一个接口RedisRepository。

通过一个Consumer接口类型的参数接收需要在两次删除缓存的代码之间执行的业务代码。

package cn.edu.sgu.www.mhxysy.redis.repository;import java.util.function.Consumer;/*** redis仓库的顶级接口:为了满足开闭原则设计了此接口* @author heyunlin* @version 1.0*/
public interface RedisRepository {void put(String key, Object value);Object get(String key);void putList(Object value);Object getList();void delete();void delete(String key);/*** 延时双删* @param key 数据的ID* @param consumer Consumer<String>*/default void delayDoubleDelete(String key, Consumer<String> consumer) { }}

RoleAccountRepository.java

package cn.edu.sgu.www.mhxysy.redis.repository.impl;import cn.edu.sgu.www.mhxysy.consts.RedisKeyPrefixes;
import cn.edu.sgu.www.mhxysy.entity.role.RoleAccount;
import cn.edu.sgu.www.mhxysy.redis.RedisUtils;
import cn.edu.sgu.www.mhxysy.redis.repository.RedisRepository;
import cn.edu.sgu.www.mhxysy.util.TimerUtils;
import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;import java.util.List;
import java.util.TimerTask;
import java.util.concurrent.TimeUnit;
import java.util.function.Consumer;/*** @author heyunlin* @version 1.0*/
@Slf4j
@Component
public class RoleAccountRepository implements RedisRepository {private final RedisUtils redisUtils;public RoleAccountRepository(RedisUtils redisUtils) {this.redisUtils = redisUtils;}@Overridepublic void put(String id, Object value) {String key = RedisKeyPrefixes.PREFIX_ROLE_ACCOUNT + id;redisUtils.set(key, JSON.toJSONString(value));redisUtils.expire(key, 7, TimeUnit.DAYS);}@Overridepublic RoleAccount get(String id) {String key = RedisKeyPrefixes.PREFIX_ROLE_ACCOUNT + id;String value = redisUtils.get(key);if (value != null) {log.debug("命中缓存{}", key);}return JSON.parseObject(value, RoleAccount.class);}@Overridepublic void putList(Object value) {String key = RedisKeyPrefixes.PREFIX_ROLE_ACCOUNTS;redisUtils.set(key, JSON.toJSONString(value), 7, TimeUnit.DAYS);}@Overridepublic List<RoleAccount> getList() {String key = RedisKeyPrefixes.PREFIX_ROLE_ACCOUNTS;String value = redisUtils.get(key);if (value != null) {log.debug("命中缓存{}", key);return JSON.parseArray(value, RoleAccount.class);}return null;}@Overridepublic void delete() {String key = RedisKeyPrefixes.PREFIX_ROLE_ACCOUNTS;redisUtils.delete(key);}@Overridepublic void delete(String key) {redisUtils.delete(RedisKeyPrefixes.PREFIX_ROLE_ACCOUNT + key);}@Overridepublic void delayDoubleDelete(String key, Consumer<String> consumer) {delete();delete(key);consumer.accept(key);// 延时双删TimerUtils.schedule(new TimerTask() {@Overridepublic void run() {delete();delete(key);}}, 500);}}

TimerUtils.java

TimerUtils是基于Timer实现的简单定时器工具类

package cn.edu.sgu.www.mhxysy.util;import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;/*** @author heyunlin* @version 1.0*/
public class TimerUtils {// 创建定时器private static final Timer timer = new Timer();/*** 延迟500毫秒执行一次定时任务* @param task 任务*/public static void schedule(TimerTask task) {schedule(task, 500);}/*** 延迟执行一次定时任务* @param task 任务* @param delay 延迟时间,单位:毫秒(ms)*/public static void schedule(TimerTask task, long delay) {timer.schedule(task, delay);}public static void main(String[] args) {// 创建定时器任务TimerTask task = new TimerTask() {@Overridepublic void run() {System.out.println("Hello world!");}};timer.schedule(task, 1000); // 1秒后执行一次timer.schedule(task, 2000, 2000); // 两秒后每两秒执行一次timer.scheduleAtFixedRate(task, 3000, 3000); // 3秒后每3秒执行一次timer.scheduleAtFixedRate(task, new Date(), 4000); // 每4秒执行一次}}

 

使用案例代码:

RoleAccountService.java

/*** @author heyunlin* @version 1.0*/
public interface RoleAccountService {/*** 通过ID删除角色* @param roleId 角色ID*/@Transactional(rollbackFor = Exception.class)void deleteById(String roleId);}

 

RoleAccountServiceImpl

/*** @author heyunlin* @version 1.0*/
@Service
public class RoleAccountServiceImpl implements RoleAccountService {private final RoleAccountMapper roleAccountMapper;private final RoleJiadianMapper roleJiadianMapper;private final SchoolSkillMapper schoolSkillMapper;private final JiadianSchemaMapper jiadianSchemaMapper;private final RoleAttributeMapper roleAttributeMapper;private final RoleAccountRepository roleAccountRepository;private final SchoolSkillCategoryMapper schoolSkillCategoryMapper;private final GangService gangService;private final ServerService serverService;private final SchoolService schoolService;private final AccountService accountService;private final RoleModelingService roleModelingService;@Autowiredpublic RoleAccountServiceImpl(RoleAccountMapper roleAccountMapper,RoleJiadianMapper roleJiadianMapper,SchoolSkillMapper schoolSkillMapper,JiadianSchemaMapper jiadianSchemaMapper,RoleAttributeMapper roleAttributeMapper,RoleAccountRepository roleAccountRepository,SchoolSkillCategoryMapper schoolSkillCategoryMapper,GangService gangService,ServerService serverService,SchoolService schoolService,AccountService accountService,RoleModelingService roleModelingService) {this.roleAccountMapper = roleAccountMapper;this.roleJiadianMapper = roleJiadianMapper;this.schoolSkillMapper = schoolSkillMapper;this.jiadianSchemaMapper = jiadianSchemaMapper;this.roleAttributeMapper = roleAttributeMapper;this.roleAccountRepository = roleAccountRepository;this.schoolSkillCategoryMapper = schoolSkillCategoryMapper;this.gangService = gangService;this.serverService = serverService;this.schoolService = schoolService;this.accountService = accountService;this.roleModelingService = roleModelingService;}@Overridepublic void deleteById(String roleId) {// 删除缓存roleAccountRepository.delayDoubleDelete(roleId, new Consumer<String>() {@Overridepublic void accept(String s) {Map<String, Object> columnMap = new HashMap<>(1);columnMap.put("role_id", s);// 删除角色属性roleAttributeMapper.deleteByRoleId(s);// 删除角色加点roleJiadianMapper.deleteByMap(columnMap);// 删除门派技能schoolSkillMapper.deleteByMap(columnMap);// 删除角色roleAccountMapper.deleteById(s);}});}}

好了,文章就分享到这里了~

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/734993.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

APP2:android studio如何使用lombok

一、前言 不知道从哪个版本开始&#xff0c;android studio便无法在plugins中下载lombok了&#xff0c;有人说是内置了&#xff0c;好像有这么回事儿。我主要面临如下两个问题&#xff1a; 使用内置lombok&#xff0c;可以自动生成setter、setter、toString等。但是&#xff0…

mediapipe 实现姿态分析——举手检测

目录 人体姿态检测 效果展示 举手检测 行业应用 代码实现 代码分析 效果展示 代码修改&#xff0c;一只手举起即可 总结 啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦^_^啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦♪(^∇^*)啦啦啦…

踏上Python之旅:从零开始,全面解析Python安装与环境配置的必知必会

在踏入Python编程殿堂之前,首当其冲的任务就是正确安装并配置Python开发环境。本文将引导您掌握Python的安装过程、不同操作系统下的最佳实践,以及如何进行多版本管理和虚拟环境设置,为您的Python开发之路打下坚实基础。 一、Python安装 Windows系统: 访问官网下载: 打开…

休息日的思考与额外题——纯纯暴力day42

文章目录 前言动态规知识点 动规五部曲一、100233. 重新分装苹果二、100247. 幸福值最大化的选择方案总结 前言 一个本硕双非的小菜鸡&#xff0c;备战24年秋招&#xff0c;计划二刷完卡子哥的刷题计划&#xff0c;加油&#xff01; 二刷决定精刷了&#xff0c;于是参加了卡子…

Linux 自动检测进程是否存活,如果挂掉自动拉起

以 jupyter 服务为例 1、检测脚本 vim /home/ailab/bin/check_ailab.sh #!/bin/bashPID$(ps -ef|grep -v grep|grep "jupyter" |awk {print $2})# 检查进程是否存在 if [ -z "$PID" ]; then# 进程不存在&#xff0c;启动进程echo "Jupyter Noteboo…

使用Vue.js开发前端项目

Vue.js是一个非常受欢迎的渐进式JavaScript框架&#xff0c;用于开发强大而互动的前端应用程序。Vue易于上手&#xff0c;同时拥有强大的功能库和灵活的生态系统。在本篇博客中&#xff0c;我将带你了解使用Vue.js开发项目的基本步骤&#xff0c;并提供相应的代码示例。 环境安…

自由职业者如何在Fiverr兼职赚美金

在这个忙碌的时代&#xff0c;大家都渴望在业余时间找到一份兼职&#xff0c;为自己带来额外的收入。然而&#xff0c;很多人常常感到困惑&#xff0c;不知道如何找到一份既赚钱又不耗费太多时间精力的兼职。今天&#xff0c;我想分享一个新的赚钱平台——Fiverr&#xff0c;让…

qt使用QAxObject操作excel程序关闭之后excel进程未被关闭的解决方案

&#x1f482; 个人主页:pp不会算法^ v ^ &#x1f91f; 版权: 本文由【pp不会算法v】原创、在CSDN首发、需要转载请联系博主 &#x1f4ac; 如果文章对你有帮助、欢迎关注、点赞、收藏(一键三连)和订阅专栏哦 今天突然发现电脑变得很卡,我以为内存占用率过高了一看才50%&…

软件测试 需求

文章目录 1. 需求1.1 什么是需求1.2 为什么要有需求1.3 测试人员眼中的需求1.4 如何深入理解需求 2. 测试用例的概念2.1 什么是测试用例2.2 为什么要有测试用例 3. 软件错误&#xff08;BUG&#xff09;的概念4. 开发模型和测试模型4.1 软件的生命周期4.2 瀑布模型&#xff08;…

【深度学习笔记】6_10 双向循环神经网络bi-rnn

注&#xff1a;本文为《动手学深度学习》开源内容&#xff0c;部分标注了个人理解&#xff0c;仅为个人学习记录&#xff0c;无抄袭搬运意图 6.10 双向循环神经网络 之前介绍的循环神经网络模型都是假设当前时间步是由前面的较早时间步的序列决定的&#xff0c;因此它们都将信…

STM32---IIC通信协议(含源码,小白进)

写在前面&#xff1a;在前面的学习过程中&#xff0c;我们学习了串口通信的USART&#xff08;通用同步异步收发器&#xff09;&#xff0c;本节我们将继续学习一种串行通信协议——IIC通信协议。之前我使用51单片机也分享过相关的IIC通信的知识&#xff0c;其实本质的知识是相通…

Spring Boot 整合 MongoDB:构建高效的数据驱动应用

Spring Boot 整合 MongoDB&#xff1a;构建高效的数据驱动应用 在现代软件开发中&#xff0c;数据持久化是应用程序不可或缺的一部分。MongoDB&#xff0c;作为一个流行的NoSQL数据库&#xff0c;以其灵活的数据模型和强大的扩展性赢得了众多开发者的青睐。Spring Boot&#x…

LeetCode_Hot100_栈_394字符串解码_Python

1.题目&#xff08;中等难度&#xff09; 给定一个经过编码的字符串&#xff0c;返回它解码后的字符串。 编码规则为: k[encoded_string]&#xff0c;表示其中方括号内部的 encoded_string 正好重复 k 次。注意 k 保证为正整数。 你可以认为输入字符串总是有效的&#xff1b…

去哪里找视频素材?这几个视频素材资源网您看看

朋友们&#xff0c;是不是在抖音短视频的创作过程中感觉自己的视频素材库就像饭后的草莓派&#xff0c;美好但不够充实&#xff1f;别担心&#xff0c;我这就给你们送上几个超级赞的素材网站&#xff0c;保证让你的创作素材库瞬间丰富起来 1&#xff0c;蛙学府 这个网站简直就…

激光SLAM-重定位方案预研

-----------------------------------------------------待更新--------------------------------------

Delphi 的Read 与Readln 的区别

结合运行窗口&#xff0c;你输入1 2 3 4 这是一行ReadLn在读入时把这四个数当成一行&#xff0c;read(a,b)只读入了前两个数&#xff1a;1 2&#xff0c;就准备读下一行了&#xff0c;下一行输入3&#xff0c;再下一行输入2&#xff0c;所以输出1232&#xff1b; Read是逐个读…

Python列表及其操作详解,从此不再迷茫!

在前面的文章中&#xff0c;我们详细讲了六大数据类型中的数字类型&#xff0c;字符串类型。相信大家都能够熟练的掌握了。那么今天我们来讲解列表&#xff08;list&#xff09;。 这是一种常用且重要的数据类型&#xff0c;List可以用来存储一系列的元素&#xff0c;对于后期…

怎么看待Groq

用眼睛看。 就是字面上的意思用眼睛看。 我属于第一波玩到的,先给大家一个直观的印象,Groq到底有多快。 目前Groq只能选Llama的70b,和Mixtral的MoE,那我选7*8的这个MoE模型来实验。 这么好些字大概花了不到1秒,流式响应,其实是不是流式已经没那么重要了 ,然后看每秒Toke…

MongoDB官网查看 MongoClient 驱动API 文档的详细步骤

目录 MongoDB官网查看 MongoClient 驱动API 文档的详细步骤1、先进入[mongodb的官网](https://www.mongodb.com/zh-cn)&#xff0c;点击【服务器文档】2、点击这个 [MongoDB Documentation](https://www.mongodb.com/docs/) 文档。3、然后点开【Java】的驱动文档4、先查看同步的…

【数据结构】详解时间复杂度和空间复杂度的计算

一、时间复杂度&#xff08;执行的次数&#xff09; 1.1时间复杂度的概念 1.2时间复杂度的表示方法 1.3算法复杂度的几种情况 1.4简单时间复杂度的计算 例一 例二 例三 1.5复杂时间复杂度的计算 例一&#xff1a;未优化冒泡排序时间复杂度 例二&#xff1a;经过优化…