iT邦幫忙

2026 iThome 鐵人賽

DAY 16
0
佛心分享-SideProject30

酒鬼加農!買醉前先來酒譜查詢器保護自己!系列 第 16 篇

[Day-16] 這杯酒出過了嗎?讓老闆賠錢的 Race Condition!

  • 分享至 

  • xImage
  •  

gh

雖然前端通常會做 debounce 或 throttle,防止使用者瘋狂請求,不過這是前端的視角!後端本身是一個獨立的程序,它要面對的是無數個使用者,因此除了流量壓力之外,資料讀寫的正確也很重要。


Race Condition

指的是同一筆資料同時被讀取並修改時發生的資料不一致問題,例如:

消費者 A 與消費者 B 在網站下單絕世好酒時,都還剩 1 個庫存,兩個人也不小心同時結帳了,資料庫也沒有做讀寫防護,因此絕世好酒的庫存變成 -1。

所以像是搶門票、週年慶搶下單、使用者網路不好所以一直連點等,多個請求同時併發送出時,都要預防 race condition 的問題,不然做生意真的會虧慘 XD


Lock

大部分的寫入功能在前面做過 transaction 了,現在要做的是防止資料同時被多個請求更新。

在查詢時就加入 .for('update')進行鎖定:

const [recipe] = await tx
  .select({ id: schema.recipes.id, likesCount: schema.recipes.likesCount })
  .from(schema.recipes)
  .where(eq(schema.recipes.id, recipeId))
  .for('update'); // row lock

在 transaction 結束前,其他請求都不能動這筆資料。

這樣的做法也被稱作悲觀鎖 (Pessimistic Lock),代表這筆資料一開始就被預期會頻繁變動,因此在查詢階段就直接使用 SQL 內建語法來鎖住,缺點是 transaction 的時間如果過長,後續的請求會耗費很長的時間等待。

有悲觀鎖就有樂觀鎖 (Optimistic Locking) 啦!樂觀鎖預期資料變動不頻繁,因此不使用鎖定語法,而改為加入一個欄位 version 或使用 updated_at。寫入時比對版本或時間,如果比對不相等就重試。

鎖定機制 情境 優點 缺點
樂觀鎖 寫入少 1. 不需額外使用鎖語法,併發效能高2. 不會產生 deadlock 1. 寫入衝突高時會頻繁重試,消耗 CPU2. 需要額外實作版號或時間戳機制
悲觀鎖 寫入多 1. 嚴格保證資料一致性2. 避免了重試成本 1. 併發效能較差,長時間佔用資源2. 容易發生 deadlock

小結

  1. 對於所有的資料寫入,都要小心一致性的問題
  2. 善用 lock 的觀念來保護資料

上一篇
[Day-15] 通關密語是餅乾!驗過的下酒菜才能吃!
系列文
酒鬼加農!買醉前先來酒譜查詢器保護自己! 共 16 篇
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言