面试官问:Redis的操作如何与数据库事务保持一致

  • 2019 年 12 月 17 日
  • 笔记

  • redis如何与数据库保持一致性的问题?

场景:如果我们在开发过程中遇到这样的一种情况,我们删除 redis中token 的同时 也需要修改数据库中 储存的 token 的状态为不可用的状态。如果这个时候我们不做处理的话,通常是先删除redis中的token,然后在进行数据库的修改。但是如果这个时候redis中的token删除成功了,但是在执行数据库操作之气程序报错了。那这个时候redis中的token已经被删除了,但是数据库中的token状态还是可用的状态,这个时候就导致了数据不一致的问题。

这时候我们需要使用统一的事务来进行解决这个问题,.但是如果只是单纯的使用数据库事务并不能解决这个问题,因为这个操作也涉及到了redis,所以这个时候我们应该使用 redis事务+数据库的事务 来保证事务一致性的问题。

如果只是单纯添加了 @Transactional(声明式事务)只能保证数据库的数据一致性问题,但是是无法控制redis中的事务的。redis中也是存在事务的。

解决方案:

我们可以使用自定义方法使用编程式事务 我们使用 begin(即控制reids事务也控制数据库事务)、commit、rollback 都需要实现控制redis事务和数据库事务。

下面直接上代码 ,

  • 这个类包装了redis事务和数据库事务,一起开启事务,一起提交事务,一起回滚事务
  • redisUtils相关代码:
  • 在写业务代码时 大致这样写

代码如下 自己copy用:

RedisDataSoureceTransaction 代码:

@Component  @Scope(ConfigurableListableBeanFactory.SCOPE_PROTOTYPE)  public class RedisDataSoureceTransaction {   @Autowired   private RedisUtil redisUtil;   /**   * 数据源事务管理器   */   @Autowired   private DataSourceTransactionManager dataSourceTransactionManager;   /**   * 开始事务 采用默认传播行为   *   * @return   */   public TransactionStatus begin() {   // 手动begin数据库事务   TransactionStatus transaction = dataSourceTransactionManager.getTransaction(new DefaultTransactionAttribute());   redisUtil.begin();//   return transaction;   }   /**   * 提交事务   *   * @param transactionStatus   * 事务传播行为   * @throws Exception   */   public void commit(TransactionStatus transactionStatus) throws Exception {   if (transactionStatus == null) {   throw new Exception("transactionStatus is null");   }   // 支持Redis与数据库事务同时提交   dataSourceTransactionManager.commit(transactionStatus);   redisUtil.exec();   }   /**   * 回滚事务   *   * @param transactionStatus   * @throws Exception   */   public void rollback(TransactionStatus transactionStatus) throws Exception {   if (transactionStatus == null) {   throw new Exception("transactionStatus is null");   }   dataSourceTransactionManager.rollback(transactionStatus);   redisUtil.discard();   }

redisutil相关代码:

/**   * 开启Redis 事务   *   * @param isTransaction   */  public void begin() {   // 开启Redis 事务权限   stringRedisTemplate.setEnableTransactionSupport(true);   // 开启事务   stringRedisTemplate.multi();  }  /**   * 提交事务   *   * @param isTransaction   */  public void exec() {   // 成功提交事务   stringRedisTemplate.exec();  }  /**   * 回滚Redis 事务   */  public void discard() {   stringRedisTemplate.discard();  }

业务的大致模板代码:

@authwared  private RedisDataSoureceTransaction manualTransaction    TransactionStatus transactionStatus = null;  try {   // // ####开启手动事务   transactionStatus = manualTransaction.begin();     // 删除或者更新数据库的数据   ...........(业务代码省略)     // 删除或者更新redis的值   .............(业务代码省略)     // #######提交事务   manualTransaction.commit(transactionStatus);    } catch (Exception e) {   try {   // 回滚事务   manualTransaction.rollback(transactionStatus);   } catch (Exception e1) {   }    }