赞
踩
Redis和数据库的数据一致性在某些场景下非常重要,如何最大程度保证Reids和数据库之间的数据一致呢?
想必大家第一时间会想到延时双删策略:
在修改数据库之前删除缓存,然后数据库中的数据修改完成后,再过一段时间再删一次缓存。
这篇文章就如何实现延时双删提供了案例代码,代码可能不是最优的,但是确实能够达到双删的效果。
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是基于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;
-
- @Autowired
- public StringRedisUtils(StringRedisTemplate stringRedisTemplate) {
- this.stringRedisTemplate = stringRedisTemplate;
- }
-
- /**
- * get key
- * @param key 键
- * @return String
- */
- @Override
- public String get(String key) {
- return getValueOperations().get(key);
- }
-
- /**
- * set key value
- * @param key 键
- * @param value 值
- */
- @Override
- public 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 键
- */
- @Override
- public void delete(String key) {
- stringRedisTemplate.delete(key);
- }
-
- /**
- * key的值自增:incrby key
- * @param key 键
- * @return 自增后的值
- */
- @Override
- public Long incrBy(String key) {
- return getValueOperations().increment(key);
- }
-
- @Override
- public Boolean hasKey(String key) {
- return stringRedisTemplate.hasKey(key);
- }
-
- /**
- * expire key seconds
- * @param key 键
- * @param timeout 过期的时间
- * @param timeUnit 时间单位
- */
- @Override
- public 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();
- }
-
- }
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) { }
-
- }
- 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;
- }
-
- @Override
- public 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);
- }
-
- @Override
- public 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);
- }
-
- @Override
- public void putList(Object value) {
- String key = RedisKeyPrefixes.PREFIX_ROLE_ACCOUNTS;
-
- redisUtils.set(key, JSON.toJSONString(value), 7, TimeUnit.DAYS);
- }
-
- @Override
- public 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;
- }
-
- @Override
- public void delete() {
- String key = RedisKeyPrefixes.PREFIX_ROLE_ACCOUNTS;
-
- redisUtils.delete(key);
- }
-
- @Override
- public void delete(String key) {
- redisUtils.delete(RedisKeyPrefixes.PREFIX_ROLE_ACCOUNT + key);
- }
-
- @Override
- public void delayDoubleDelete(String key, Consumer<String> consumer) {
- delete();
- delete(key);
-
- consumer.accept(key);
-
- // 延时双删
- TimerUtils.schedule(new TimerTask() {
- @Override
- public void run() {
- delete();
- delete(key);
- }
- }, 500);
- }
-
- }
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() {
- @Override
- public 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秒执行一次
- }
-
- }
- /**
- * @author heyunlin
- * @version 1.0
- */
- public interface RoleAccountService {
-
- /**
- * 通过ID删除角色
- * @param roleId 角色ID
- */
- @Transactional(rollbackFor = Exception.class)
- void deleteById(String roleId);
-
- }
- /**
- * @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;
-
- @Autowired
- public 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;
- }
-
- @Override
- public void deleteById(String roleId) {
- // 删除缓存
- roleAccountRepository.delayDoubleDelete(roleId, new Consumer<String>() {
- @Override
- public 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);
- }
- });
- }
-
- }
好了,文章就分享到这里了~
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。