1. 高併發緩存架構——雪崩解決方案
- 網易雲課堂課程地址
- https://study.163.com/course/courseLearn.htm?courseId=1006355036#/learn/live?lessonId=1053884737&courseId=1006355036
Tip
性能(爲什麼要用緩存)
1. MySQL
- MySQL官方測試報告的機器性能比較完美,實際可以看阿里雲性能白皮書MySQL版
- https://help.aliyun.com/document_detail/53638.html?spm=a2c4g.11186623.6.1333.5c896fddmFtkF6
- 常用的8核16G、32G,TPS在1k左右,QPS在2w左右(TPS:是TransactionsPerSecond的縮寫,也就是事務數/秒;QPS:Queries Per Second意思是“每秒查詢率”。)
2. Redis
- Redis 性能官方文檔
- https://redis.io/topics/benchmarks
- Intel® Xeon® CPU E5520 @ 2.27GHz (without pipelining) 單核 12w/s
緩存失效的兩種場景(12306餘票查詢爲例)
- 高峯期大面積緩存key失效(所有車次查詢全部依賴數據庫)
- 解決方法簡單,對不同車次設置不同的緩存失效時間不同
- 局部高峯期,熱點緩存key失效(某一趟車次的海量請求直擊數據庫)
緩存雪崩
- 因爲緩存服務掛掉或者熱點緩存失效,所有請求都去查數據庫,導致數據庫連接不夠或者數據庫處理不過來,從而導致整個系統不可用。
解決方案
鎖
// Java中代碼實現的鎖
Lock lock = new ReentrantLock();
// 查詢方法中,緩存失效時
lock.lock();
try {
// 再次判斷緩存是否存在
// 查詢數據庫,重建緩存
} finally {
lock.unlock();
}
- 優點:簡單有效、適用範圍廣
- 缺點:阻塞其他線程,鎖的顆粒度太粗
細粒度的鎖
// 每個車次一個鎖
ConcurrentHashMap<String, String> maplock = new ConcurrentHashMap<>();
// 查詢方法中,緩存失效時
boolean lock = false;
try {
lock = maplock.putIfAbsent(ticketSeq, "true") == null;
if(lock) {
// 再次判斷緩存是否存在
// 查詢數據庫,重建緩存
} else {
// 沒拿到鎖的怎麼辦?——緩存降級,根據業務需要降級
// 方案一,返回固定值
// 方案二,讀備份緩存(操作主緩存時雙寫,備份緩存不設置過期時間)
}
} finally {
if(lock) {
maplock.remove(tiketSeq);
}
}
降級策略
- 優點:靈活多變,方便使用
- 缺點:需要開發人員掌控業務,增加維護複雜度
多線程單元測試
private static final int THREAD_NUM = 1000;
private CountDownLatch countDownLatch = new CountDownLatch(THREAD_NUM);
@Test
public void test() throws InterruptedException {
Thread[] threads = new Thread[THREAD_NUM];
for(int i=0; i<THREAD_NUM; i++) {
Thread thread = new Thread(() -> {
try {
countDownLatch.await();
ticketService.queryTicketStock("G290");
} catch (Exception e) {
e.printStackTrace();
}
});
threads[i] = thread;
thread.start();
// 倒計時計數器減1,代表又一個線程就緒了
countDownLatch.countDown();
}
// 等待上面所有線程執行完畢後,結束測試
for (Thread thread : threads) {
thread.join();
}
}