Node.js實作Mutex(互斥鎖)防止緩存擊穿
最近在工作上需要寫一隻會高併發的API。為了不讓DB被灌爆,所以在DB前面再加一層redis當緩存,並且設定TTL過期,因此整個API程式流程大致上變成:
- 從redis取資料 -> 成功取到資料 -> 返回
如果今天redis因為資料TTL過期導致取不到資料,這時候才訪問DB:
- 從redis取資料 -> 沒有取到資料 -> 從DB取資料 -> 成功取到資料,將資料回存redis並設定TTL -> 返回
轉換成程式碼的話看起來也並沒有特別的複雜:
const item = await redisClient.hgetall(itemKey);
if(item) {
return item;
}
const sql = 'SELECT * FROM `Product` WHERE `id` = ?';
const [rows, fields] = await dbConnection.query(sql, [itemKey]);
if(rows.length > 0) {
const [item] = rows;
await redisClient
.multi()
.hmset(itemKey, item)
.expire(itemKey, 60)
.exec();
return item;
}
return null;
不過,魔鬼藏在細節裡。Node.js雖然說是使用單執行緒在執行,但因為用到了async/await的語法,所以實際上當遇到異步函式時,Node.js會將異步函式丟進event loop等有空時才執行,並且當promise被resolve或reject後才從剛剛await處接續往下。這就讓在高併發的情況下,會發生如下圖中的狀況。


近期迴響