你是 Bob,一個請不起軟體工程師的創業者。在 Vibe Coding 的時代,你相信自己靠 AI 寫 MVP 沒問題的。
今天你學會架 API、做前端渲染、連接 SQL 資料庫,總算是做出了個電商 App,測試沒問題,終於可以試用期上線啦!
哇!結果一上線辦活動,你好不容易吸引來的用戶開始哀哀叫啦!
「Bob!我明明看到還有庫存,為什麼扣款了卻不出貨?!」
你去後台一看,庫存只有 10 個,卻賣出了 12 個!?你要去哪裡生出另外兩雙喬丹限量鞋?
你汗流浹背了。你可不想讓人發現你只是個摳門的創業者,這牌子剛打磨好就要砸啦! 你趕快跑來問後端工程師 Aaron。
「Aaron Aaron,這是甚麼情形?難道天要亡我,電腦就這麼故意多算兩筆數據嗎?」
Aaron 拍拍你的背,「這不是運氣不好,這是典型的 Race Condition (競態條件)。」
「Race Condition?」
🛑 為什麼你的程式碼會說謊?
Bob,你寫的邏輯大概是長這樣吧?
- 讀取:看資料庫,還有貨嗎? (
Select count...) - 判斷:如果 > 0,就賣給他。
- 寫入:把庫存 -1,寫回資料庫。 (
Update...)
這邏輯在只有你一個人測試時,完美無缺。 但當 User A 和 User B 在「同一毫秒」按下購買鍵時,悲劇發生了:
- A 看到庫存是 1。
- B 同時也看到庫存是 1 (因為 A 還沒來得及扣掉)。
- A 買到了,庫存變 0。
- B 也以為自己買到了,庫存變 -1。
這就是 Race Condition。
最直觀的解決方法,當然就是用鎖 (Lock) 擋住資料庫,讓顧客的需求一個一個排隊等著處理。 然而你的資料庫需要花時間,慢慢把資料寫在硬碟!所以在高併發下,根本來不及擋住這群喪屍般的用戶。
✅ 業界標準的解法:原子操作 (Atomic Operation)
在專業的後端架構中,我們處理這種「錢」跟「量」的問題,通常不會讓關聯式資料庫 (SQL) 站第一線。我們會請出一位守門員:Redis。
我們要利用 Redis 的一個特性:單執行緒與原子性 (Atomicity)。
想像 Redis 是一個 「只有單一窗口的售票亭」。 不管外面有一萬個人同時擠進來,Redis 會強迫所有人「排成一直線」。
我們使用 Redis 的 DECR (遞減) 指令:
- User A 來了 ➡️ Redis 說:扣 1,剩 0。成功。
- User B 同一毫秒來了 ➡️ Redis 說:你排隊。輪到你時,已經剩 0 了,扣完變 -1。失敗,滾蛋。
你搔了搔腦袋:「那不是就和資料庫上鎖一樣嗎?」
Aaron 搖了搖頭:「大不一樣!Redis 因為全部的資料都寫在 RAM (記憶體) 裡面,且經過各式各樣的優化,速度快多了!」
💡 Aaron 的實戰總結
你聽完眼神發亮:「所以我只要加個 Redis 就可以解決問題了?」
「沒錯,但這只是第一步。」Aaron 喝了一口咖啡,「重點不在於工具,而在於架構思維。你要學會區分:
- 快取層 (Redis):用來扛流量、擋子彈、做秒殺判定。
- 資料庫 (DB):用來存那些『已經確定成功』的訂單。」
你沉思許久:「原來,我只學會了使用基礎的工具啊!後面還有好多要學!」 Aaron 點了點頭:「沒錯,但究竟要用到多少複雜的架構,就視你的生意而定了。一切都是為了守護你的商業信譽。」
聽完解決方案,你趕緊回家熬夜改 Code 了。Aaron 心想,希望 Bob 這次不會把 Redis 的記憶體塞爆… 但那又是另一個故事了。
[Next]
你突然想到:「既然 Redis 這麼強,那我可以把 SQL 資料庫丟了嗎?」 下一篇我們來聊聊:資料的持久化風險,與為什麼我們仍需要傳統資料庫。
👇 想看我如何實作這套防超賣系統? 我是 Aaron,一名在美國的軟體工程師。我正在把這套架構寫成簡單的開源專案 “FlashForm”,供人參考。 如果你喜歡這種「解決真實商業問題」的架構筆記,歡迎持續關注追蹤。
#SystemDesign #Startup #RaceCondition #Backend #AaronWuBuilds