有柳巖問:高并發(fā)庫存扣減一致性問題,怎么用 Redis 解決?
昨天和大家聊了庫存異常的兩種情況:
- “先查后減”,容易出現(xiàn)異常;
- “先查后設(shè)”,冪等性優(yōu)化,解決重試問題;
- “先查后設(shè),有條件的設(shè)”,CAS優(yōu)化,解決并發(fā)問題;

有留言說:可以用redis優(yōu)化。redis方案是可以的,今天簡單展開說說。
redis一般如何操作庫存?
一般在redis客戶端執(zhí)行:
$num = GET key$num = $num - $countSET key $num這樣操作存在什么問題?
在并發(fā)量大的時候,會遇到和上一篇文章中提到的并發(fā)一致性問題。
是如何利用redis事務(wù)操作優(yōu)化?
本質(zhì)也是樂觀鎖。
redis的WATCH和EXEC可以提供類似事務(wù)的機(jī)制:
- WATCH觀察key是否被改動;
- 如果提交時key被改動,EXEC將返回null,表示事務(wù)失敗;
保證一致性的庫存扣減可以優(yōu)化為:
WATCH key$num = GET key$num = $num - $countMULTISET key $numEXEC在WATCH之后,EXEC執(zhí)行之前,如果key的值發(fā)生變化,則EXEC會失敗。
redis的WATCH為何能夠保證事務(wù)性?
本質(zhì)上,和上一篇文章中提到的樂觀鎖CAS機(jī)制是一樣的,詳見:
大部分情況下,redis不同的客戶端會訪問不同的key,所以WATCH碰撞的概率會比較小,在秒殺的業(yè)務(wù)場景,使用WATCH,也會有一定的沖突,需要針對秒殺業(yè)務(wù)做單獨的優(yōu)化。
為什么redis更能應(yīng)對超高并發(fā)的庫存管理?
根本原因,還是redis內(nèi)存訪問與mysql數(shù)據(jù)落盤的性能差異。
redis庫存管理有什么需要注意的?
數(shù)據(jù)具備“易失性”,如果重啟,數(shù)據(jù)可能丟失,所以redis大部分時候是用來存儲允許cache miss的數(shù)據(jù)。如果實在要用redis來存儲業(yè)務(wù)上不能夠丟失的數(shù)據(jù),需要重點設(shè)計一致性與可用性。
當(dāng)然,redis也可以固化數(shù)據(jù),但如果把redis當(dāng)做DB用,為什么不直接使用mysql呢?
稍作總結(jié)
- 可以使用redis的事務(wù)性扣減庫存,其核心原理也是CAS機(jī)制;
- redis高性能的核心是內(nèi)存存儲,一般用來存儲允許cache miss的數(shù)據(jù);
- 如果使用redis存儲不允許丟失的數(shù)據(jù),需要注意一致性與可用性,這一點上,對比mysql沒有額外優(yōu)勢;
具體怎么用,還得結(jié)合業(yè)務(wù)折衷。
任何脫離業(yè)務(wù)的架構(gòu)設(shè)計都是耍流氓!
知其然,知其所以然。
思路比結(jié)論更重要。


































