redis快取恢復-2022新項目
- 2022 年 9 月 3 日
- 筆記
- Mysql-Oracle-postgresql-資料庫相關
一、業務場景
Web項目開發中,為了加快數據處理的的效率,大量的使用了各種快取,快取技術主要使用的是redis。導致出現的小小的
問題是對redis快取形成了一個比較強的依賴,並且有的數據暫時是沒有同步到業務資料庫當中進行存儲的,有不少數據都是直
接從快取中獲取。這種處理方式確實加快了數據的處理效率,可是也存在一些問題。
二、需求分析
當前由於系統要進行遷移,以前保存在一台伺服器上面的redis中的數據,是不可能遷移的。即使運維人員願意遷移,操作起來
也是相當的麻煩。還有一個問題,如果發現快取中的數據存在異常,如何使用某種機制去自動更新快取或者是手動更新快取數據,讓
快取中的數據和資料庫中的最終趨於一致,並且保證數據的正確性呢?這就需要做快取恢復。
三、解決方案
快取恢復只針對時效性比較長的數據,如果某些快取數據幾分鐘更新一次,或者十幾分鐘更新一次,這種快取數據就可以考慮不用
恢復。對於某些沒有失效時間限制的一些快取數據,就需要做這種處理。需要使用的地方:一種情況是系統遷移的時候;一種情況是快取
數據出現問題,需要採取某種措施更新快取的時候。下面就來聊聊兩種快取更新的方式。
.方式一:通過介面請求更新快取.
這種方式比較常規,就是寫一個普通的介面,發一個請求到後台,傳遞對應的參數進行快取更新操作。這種操作方式是立馬全量更新
所有的快取數據。對於數據量不大的業務比較適合,更新的時間也不會太久,預估可能就一兩分鐘的樣子。可是當數據量很大的時候,
比如少說幾十萬甚至更多的數據都需要添加到快取中,這種應該如何處理呢?
public boolean updateTest() {
Map<Integer, List<TransmitActivityTotal>> transmitActivityTotalMap = new HashMap<>(30);
List<TransmitActivityTotal> transmitActivityTotals = null;
StopWatch stopWatch = new StopWatch();
stopWatch.start();
try {
// 查詢數據
for(int i = 0; i < 30; i++){
// 根據不同的分表資訊查詢不同的數據
transmitActivityTotals = null;
transmitActivityTotalMap.put(i, transmitActivityTotals);
}
// 處理數據
if (transmitActivityTotalMap.isEmpty()) {
return false;
}
Map<Long, Integer> idTotalMap = new HashMap<>();
Integer total = null;
for(Map.Entry<Integer, List<TransmitActivityTotal>> item : transmitActivityTotalMap.entrySet()) {
for(TransmitActivityTotal transmitActivityTotal : item.getValue()) {
total = transmitActivityTotal.getTotal() == null ? Integer.valueOf(0) : transmitActivityTotal.getTotal();
if (idTotalMap.keySet().contains(transmitActivityTotal.getId())) {
// 再次添加 原有數據 + 新數據
idTotalMap.put(transmitActivityTotal.getId(), idTotalMap.get(transmitActivityTotal.getId()) + total);
} else {
// 首次添加
idTotalMap.put(transmitActivityTotal.getId(), total);
}
}
}
for(Map.Entry<Long, Integer> item : idTotalMap.entrySet()) {
// 使用redis進行 恢復快取 操作
log.info("id-->{}, 數量--->", item.getKey(), item.getValue());
}
stopWatch.stop();
log.info("快取恢復耗時--->{}毫秒", stopWatch.getLastTaskTimeMillis());
return true;
} catch (Exception ex) {
stopWatch.stop();
log.error("快取更新異常--->{}", ex);
return false;
}
}
方式二:通過數據字典表來控制快取的更新。
自己目前所參與開發的項目,在項目啟動的時候,會將所有的數據字典資訊添加到快取當中,並且會隔幾分鐘去更新一次數據字典數據。
就是利用這一點來增量更新快取資訊,比如每次只更新某個用戶相關的操作數據,而不是一次性更新所有用戶的數據。減輕了伺服器的壓力,
這種處理方式也更加的符合實際需求。舉一個簡單的示例,首先考慮兼容性問題,在設計的時候可以在數據字典中添加一個值,當這個值
為OLD的時候,就使用舊有的邏輯,直接從快取中獲取數據;如果是新系統,則可以在頁面中將這個數據字典表的值設置為NEW,這時就
使用新的邏輯,先從資料庫中去查詢數據,然後將查詢結果保存在快取中。這樣設計就能夠兼容之前的系統,也可以進行系統的遷移。這裡
還存在一個問題,就是使用新系統時,不可能每次都去查詢資料庫,然後將數據存入快取中。這時就可以添加第二個數據字典值,稱它為數
據版本吧,比如設置為100,也是先從快取中獲取這個數據字典值a,然後從快取中獲取獲取數據版本值b,判斷值a和值b是否一致。一致
則直接返回快取數據。不一致則查詢資料庫,然後更新快取,並且將快取中的數據版本的值更新為數據字典的值。這樣在後台管理系統中,
更新數據版本的值,就可以動態的更新快取數據的值,並且每次更新的快取數據量很少。
這種設計稍微有些複雜,還是需要好好理解,自己最開始的時候也是沒有理解這種快取恢復的方式。去詢問負責人之後,才搞清楚是如何
進行設計的。明白之後才開始寫程式碼,最後進行測試,測試結論通過。