面試官問: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) { } }