面试官问: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) { } }