每天分享java乾貨,歡迎關注! 你的成功源於點點滴滴!
- 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) { } }