一.問題簡述
在並發低、用戶少的情況下,每次查詢都能去Mysql查詢數據返回,但在高並發情況下,每一個讀請求都到Mysql去查詢會導致數據庫壓力太大。
所以一般會使用Redis做一個緩沖,減輕數據庫的壓力:
正常情況下,使用Redis緩存數據流程如下:
正常的讀請求該模式不會有問題,但是如果數據庫信息有改動,那么數據庫和Redis的數據一致性如何保證?
按我們常規的邏輯,數據庫修改后去刪除Redis的key即可。
其實不管是先刪Redis再修改庫,還是先修改庫再刪Redis都會有問題,因為讀線程和寫線程是並發的,無法保證其執行順序。
1.如果先刪Redis緩存,再寫庫。在刪除Redis緩存后,寫線程還沒來得及修改數據,這時讀線程發現緩存為空,則取讀取了臟數據。
2.如果先修改數據,在刪Redis緩存。在修改數據后如果線程宕機,那么Redis緩存刪除失敗,也會出現臟數據。
二.延時雙刪策略+設置緩存過期時間
所謂雙刪策略,就是在更新庫的前后都進行Redis緩存的刪除,但是更新后的刪除時延時刪除,偽代碼如下:
public void update(){
//1.刪除Redis緩存
redisTemplate.delete(key);
//2.更新數據庫
mysqlConnection.update();
//3.延時刪除
Thread.sleep(200);
redisTemplate.delete(key);
}
等待的時間如何確定
等待的時間為讀線程讀取數據的一個耗時,包括Redis主從同步,網絡耗時等。
為什么要使用延時刪除
為了防止在更新數據的過程中有讀線程進來緩存數據,所以更新后估算一個時間再刪除Redis緩存。
為什么要設置緩存過期時間
設置緩存時間是保證數據一致性的最終解決方案,因為在最壞情況下,臟數據的時間最多為設置的過期時間。過期后讀線程會到數據庫讀取新數據。
該方案有什么弊端
1. 最壞情況是在一個過期時間內會存在臟數據。
2. 因為有延時,所以增加的寫線程的執行時間。
三.基於訂閱binglog的同步機制
后續完善