当前位置:   article > 正文

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

redis延迟双删实现

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

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

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

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

具体代码实现:

RedisUtils.java

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

  1. package cn.edu.sgu.www.mhxysy.redis;
  2. import java.util.concurrent.TimeUnit;
  3. /**
  4. * redis工具类
  5. * @author heyunlin
  6. * @version 1.0
  7. */
  8. public interface RedisUtils {
  9. String get(String key);
  10. void set(String key, String value);
  11. void set(String key, String value, long timeout, TimeUnit timeUnit);
  12. void delete(String key);
  13. Long incrBy(String key);
  14. Boolean hasKey(String key);
  15. void expire(String key, long timeout, TimeUnit timeUnit);
  16. }

StringRedisUtils.java

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

  1. package cn.edu.sgu.www.mhxysy.redis;
  2. import org.springframework.beans.factory.annotation.Autowired;
  3. import org.springframework.data.redis.core.StringRedisTemplate;
  4. import org.springframework.data.redis.core.ValueOperations;
  5. import org.springframework.stereotype.Component;
  6. import java.util.concurrent.TimeUnit;
  7. /**
  8. * 封装了StringRedisTemplate的redis工具类
  9. * @author heyunlin
  10. * @version 1.0
  11. */
  12. @Component
  13. public class StringRedisUtils implements RedisUtils {
  14. private final StringRedisTemplate stringRedisTemplate;
  15. @Autowired
  16. public StringRedisUtils(StringRedisTemplate stringRedisTemplate) {
  17. this.stringRedisTemplate = stringRedisTemplate;
  18. }
  19. /**
  20. * get key
  21. * @param key 键
  22. * @return String
  23. */
  24. @Override
  25. public String get(String key) {
  26. return getValueOperations().get(key);
  27. }
  28. /**
  29. * set key value
  30. * @param key 键
  31. * @param value 值
  32. */
  33. @Override
  34. public void set(String key, String value) {
  35. getValueOperations().set(key, value);
  36. }
  37. /**
  38. * set key value
  39. * expire key seconds
  40. * @param key 键
  41. * @param value 值
  42. * @param timeout 过期的时间
  43. * @param timeUnit 时间单位
  44. */
  45. public void set(String key, String value, long timeout, TimeUnit timeUnit) {
  46. getValueOperations().set(key, value, timeout, timeUnit);
  47. }
  48. /**
  49. * del key
  50. * @param key 键
  51. */
  52. @Override
  53. public void delete(String key) {
  54. stringRedisTemplate.delete(key);
  55. }
  56. /**
  57. * key的值自增:incrby key
  58. * @param key 键
  59. * @return 自增后的值
  60. */
  61. @Override
  62. public Long incrBy(String key) {
  63. return getValueOperations().increment(key);
  64. }
  65. @Override
  66. public Boolean hasKey(String key) {
  67. return stringRedisTemplate.hasKey(key);
  68. }
  69. /**
  70. * expire key seconds
  71. * @param key 键
  72. * @param timeout 过期的时间
  73. * @param timeUnit 时间单位
  74. */
  75. @Override
  76. public void expire(String key, long timeout, TimeUnit timeUnit) {
  77. stringRedisTemplate.expire(key, timeout, timeUnit);
  78. }
  79. /**
  80. * 获取ValueOperations对象
  81. * @return ValueOperations<String, String>
  82. */
  83. private ValueOperations<String, String> getValueOperations() {
  84. return stringRedisTemplate.opsForValue();
  85. }
  86. }

RedisRepository.java

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

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

  1. package cn.edu.sgu.www.mhxysy.redis.repository;
  2. import java.util.function.Consumer;
  3. /**
  4. * redis仓库的顶级接口:为了满足开闭原则设计了此接口
  5. * @author heyunlin
  6. * @version 1.0
  7. */
  8. public interface RedisRepository {
  9. void put(String key, Object value);
  10. Object get(String key);
  11. void putList(Object value);
  12. Object getList();
  13. void delete();
  14. void delete(String key);
  15. /**
  16. * 延时双删
  17. * @param key 数据的ID
  18. * @param consumer Consumer<String>
  19. */
  20. default void delayDoubleDelete(String key, Consumer<String> consumer) { }
  21. }

RoleAccountRepository.java

  1. package cn.edu.sgu.www.mhxysy.redis.repository.impl;
  2. import cn.edu.sgu.www.mhxysy.consts.RedisKeyPrefixes;
  3. import cn.edu.sgu.www.mhxysy.entity.role.RoleAccount;
  4. import cn.edu.sgu.www.mhxysy.redis.RedisUtils;
  5. import cn.edu.sgu.www.mhxysy.redis.repository.RedisRepository;
  6. import cn.edu.sgu.www.mhxysy.util.TimerUtils;
  7. import com.alibaba.fastjson.JSON;
  8. import lombok.extern.slf4j.Slf4j;
  9. import org.springframework.stereotype.Component;
  10. import java.util.List;
  11. import java.util.TimerTask;
  12. import java.util.concurrent.TimeUnit;
  13. import java.util.function.Consumer;
  14. /**
  15. * @author heyunlin
  16. * @version 1.0
  17. */
  18. @Slf4j
  19. @Component
  20. public class RoleAccountRepository implements RedisRepository {
  21. private final RedisUtils redisUtils;
  22. public RoleAccountRepository(RedisUtils redisUtils) {
  23. this.redisUtils = redisUtils;
  24. }
  25. @Override
  26. public void put(String id, Object value) {
  27. String key = RedisKeyPrefixes.PREFIX_ROLE_ACCOUNT + id;
  28. redisUtils.set(key, JSON.toJSONString(value));
  29. redisUtils.expire(key, 7, TimeUnit.DAYS);
  30. }
  31. @Override
  32. public RoleAccount get(String id) {
  33. String key = RedisKeyPrefixes.PREFIX_ROLE_ACCOUNT + id;
  34. String value = redisUtils.get(key);
  35. if (value != null) {
  36. log.debug("命中缓存{}", key);
  37. }
  38. return JSON.parseObject(value, RoleAccount.class);
  39. }
  40. @Override
  41. public void putList(Object value) {
  42. String key = RedisKeyPrefixes.PREFIX_ROLE_ACCOUNTS;
  43. redisUtils.set(key, JSON.toJSONString(value), 7, TimeUnit.DAYS);
  44. }
  45. @Override
  46. public List<RoleAccount> getList() {
  47. String key = RedisKeyPrefixes.PREFIX_ROLE_ACCOUNTS;
  48. String value = redisUtils.get(key);
  49. if (value != null) {
  50. log.debug("命中缓存{}", key);
  51. return JSON.parseArray(value, RoleAccount.class);
  52. }
  53. return null;
  54. }
  55. @Override
  56. public void delete() {
  57. String key = RedisKeyPrefixes.PREFIX_ROLE_ACCOUNTS;
  58. redisUtils.delete(key);
  59. }
  60. @Override
  61. public void delete(String key) {
  62. redisUtils.delete(RedisKeyPrefixes.PREFIX_ROLE_ACCOUNT + key);
  63. }
  64. @Override
  65. public void delayDoubleDelete(String key, Consumer<String> consumer) {
  66. delete();
  67. delete(key);
  68. consumer.accept(key);
  69. // 延时双删
  70. TimerUtils.schedule(new TimerTask() {
  71. @Override
  72. public void run() {
  73. delete();
  74. delete(key);
  75. }
  76. }, 500);
  77. }
  78. }

TimerUtils.java

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

  1. package cn.edu.sgu.www.mhxysy.util;
  2. import java.util.Date;
  3. import java.util.Timer;
  4. import java.util.TimerTask;
  5. /**
  6. * @author heyunlin
  7. * @version 1.0
  8. */
  9. public class TimerUtils {
  10. // 创建定时器
  11. private static final Timer timer = new Timer();
  12. /**
  13. * 延迟500毫秒执行一次定时任务
  14. * @param task 任务
  15. */
  16. public static void schedule(TimerTask task) {
  17. schedule(task, 500);
  18. }
  19. /**
  20. * 延迟执行一次定时任务
  21. * @param task 任务
  22. * @param delay 延迟时间,单位:毫秒(ms)
  23. */
  24. public static void schedule(TimerTask task, long delay) {
  25. timer.schedule(task, delay);
  26. }
  27. public static void main(String[] args) {
  28. // 创建定时器任务
  29. TimerTask task = new TimerTask() {
  30. @Override
  31. public void run() {
  32. System.out.println("Hello world!");
  33. }
  34. };
  35. timer.schedule(task, 1000); // 1秒后执行一次
  36. timer.schedule(task, 2000, 2000); // 两秒后每两秒执行一次
  37. timer.scheduleAtFixedRate(task, 3000, 3000); // 3秒后每3秒执行一次
  38. timer.scheduleAtFixedRate(task, new Date(), 4000); // 每4秒执行一次
  39. }
  40. }

 

使用案例代码:

RoleAccountService.java

  1. /**
  2. * @author heyunlin
  3. * @version 1.0
  4. */
  5. public interface RoleAccountService {
  6. /**
  7. * 通过ID删除角色
  8. * @param roleId 角色ID
  9. */
  10. @Transactional(rollbackFor = Exception.class)
  11. void deleteById(String roleId);
  12. }

 

RoleAccountServiceImpl

  1. /**
  2. * @author heyunlin
  3. * @version 1.0
  4. */
  5. @Service
  6. public class RoleAccountServiceImpl implements RoleAccountService {
  7. private final RoleAccountMapper roleAccountMapper;
  8. private final RoleJiadianMapper roleJiadianMapper;
  9. private final SchoolSkillMapper schoolSkillMapper;
  10. private final JiadianSchemaMapper jiadianSchemaMapper;
  11. private final RoleAttributeMapper roleAttributeMapper;
  12. private final RoleAccountRepository roleAccountRepository;
  13. private final SchoolSkillCategoryMapper schoolSkillCategoryMapper;
  14. private final GangService gangService;
  15. private final ServerService serverService;
  16. private final SchoolService schoolService;
  17. private final AccountService accountService;
  18. private final RoleModelingService roleModelingService;
  19. @Autowired
  20. public RoleAccountServiceImpl(
  21. RoleAccountMapper roleAccountMapper,
  22. RoleJiadianMapper roleJiadianMapper,
  23. SchoolSkillMapper schoolSkillMapper,
  24. JiadianSchemaMapper jiadianSchemaMapper,
  25. RoleAttributeMapper roleAttributeMapper,
  26. RoleAccountRepository roleAccountRepository,
  27. SchoolSkillCategoryMapper schoolSkillCategoryMapper,
  28. GangService gangService,
  29. ServerService serverService,
  30. SchoolService schoolService,
  31. AccountService accountService,
  32. RoleModelingService roleModelingService) {
  33. this.roleAccountMapper = roleAccountMapper;
  34. this.roleJiadianMapper = roleJiadianMapper;
  35. this.schoolSkillMapper = schoolSkillMapper;
  36. this.jiadianSchemaMapper = jiadianSchemaMapper;
  37. this.roleAttributeMapper = roleAttributeMapper;
  38. this.roleAccountRepository = roleAccountRepository;
  39. this.schoolSkillCategoryMapper = schoolSkillCategoryMapper;
  40. this.gangService = gangService;
  41. this.serverService = serverService;
  42. this.schoolService = schoolService;
  43. this.accountService = accountService;
  44. this.roleModelingService = roleModelingService;
  45. }
  46. @Override
  47. public void deleteById(String roleId) {
  48. // 删除缓存
  49. roleAccountRepository.delayDoubleDelete(roleId, new Consumer<String>() {
  50. @Override
  51. public void accept(String s) {
  52. Map<String, Object> columnMap = new HashMap<>(1);
  53. columnMap.put("role_id", s);
  54. // 删除角色属性
  55. roleAttributeMapper.deleteByRoleId(s);
  56. // 删除角色加点
  57. roleJiadianMapper.deleteByMap(columnMap);
  58. // 删除门派技能
  59. schoolSkillMapper.deleteByMap(columnMap);
  60. // 删除角色
  61. roleAccountMapper.deleteById(s);
  62. }
  63. });
  64. }
  65. }

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

声明:本文内容由网友自发贡献,转载请注明出处:【wpsshop博客】
推荐阅读
相关标签
  

闽ICP备14008679号