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