Redis自增功能解決全局唯一ID
- 如果用MySQL的自增長ID,ID的規(guī)律性太明顯,會暴漏一些信息(比如銷量等)
- 數(shù)據(jù)量太大時(shí)一張表存不下,需要多張表,MySQL多張表的自增長都是獨(dú)立的,會出現(xiàn)重復(fù)ID
- 需要一種在分布式系統(tǒng)下可以生成全局唯一ID的工具,必須唯一且遞增
- 在某項(xiàng)目里,不管數(shù)據(jù)庫的表有多少個(gè),Redis只有一個(gè),因此Redis遞增功能生成的ID一定是全局唯一的
- 為了保證遞增的同時(shí)且沒有規(guī)律,保證安全性,可以在Redis自增數(shù)值的基礎(chǔ)上拼接一些其它信息
Redis實(shí)現(xiàn)優(yōu)惠券秒殺的主要思路
文章來源:http://www.zghlxwxcb.cn/news/detail-687664.html
實(shí)現(xiàn)過程中出現(xiàn)的問題及解決方法
超賣問題
- 在高并發(fā)場景下,多個(gè)線程同時(shí)操作共享的資源(庫存),導(dǎo)致實(shí)際賣出的數(shù)量超出了庫存數(shù)量
方案1 悲觀鎖
- 態(tài)度比較悲觀,認(rèn)為線程安全問題肯定會發(fā)生,在操作數(shù)據(jù)之前提前獲取鎖
- 例子:Synchronized、Lock
- 優(yōu)點(diǎn):安全性高
- 缺點(diǎn):性能低,實(shí)現(xiàn)簡單
方案2 樂觀鎖
- 態(tài)度比較樂觀,認(rèn)為線程安全問題不一定會發(fā)生,因此不加鎖,只在數(shù)據(jù)更新時(shí)去判斷在它之前有沒有其它線程修改數(shù)據(jù)。如果沒有修改認(rèn)為是安全的,直接更新數(shù)據(jù),如果已經(jīng)被修改說明不安全,重試或報(bào)異常
- 版本號法:給庫存增加一個(gè)版本字段,線程1查詢并記錄下庫存和版本號,然后將庫存-1,版本號+1,來表示線程1修改了一次數(shù)據(jù),然后在更新數(shù)據(jù)之前再判斷一下版本號,是否是自己當(dāng)時(shí)記錄的版本號+1,若是,說明沒有并發(fā)線程在期間修改過數(shù)據(jù),安全,可以放心更新,若不是,說明正好有并發(fā)線程在期間修改過了數(shù)據(jù),不安全,重試或者報(bào)異常
- CAS法:版本號法的簡化版本,去掉版本號這個(gè)多余的字段,直接用庫存本身代替版本號,根據(jù)庫存本身有沒有發(fā)生變化來確定是否更新
- 優(yōu)點(diǎn):性能高
- 缺點(diǎn):實(shí)現(xiàn)復(fù)雜
一人一單問題
- 常見的業(yè)務(wù)問題,要求同一個(gè)優(yōu)惠券,一個(gè)用戶只能下一單
- 在庫存充足判斷成功后再增加一個(gè)判斷,用用戶ID和優(yōu)惠券ID聯(lián)合查詢,來判斷該用戶是否已經(jīng)買過一優(yōu)惠券
- 在單機(jī)模式下,可以加Synchronized鎖來保證線程安全
- 在集群模式下,Synchronized鎖無效,需要用分布式鎖來確保線程安全。Synchronized鎖無效的原因是因?yàn)槊颗_服務(wù)器有自己的常量池,鎖監(jiān)視器便保存在常量池中,用戶嘗試獲取鎖便是訪問鎖監(jiān)視器,因此,主要問題是因?yàn)槎鄠€(gè)服務(wù)器的鎖監(jiān)視器是獨(dú)立的,所以多個(gè)服務(wù)器上的用戶能在同一時(shí)刻同時(shí)獲取鎖,進(jìn)而導(dǎo)致線程安全問題
分布式鎖
- 在單機(jī)情況下,只有一個(gè)JVM,JVM中只有一個(gè)鎖監(jiān)視器,只有一個(gè)程序可以獲取到鎖。但在集群情況下,有多個(gè)JVM,多個(gè)JVM中有多個(gè)鎖監(jiān)視器,程序可以獲取到多個(gè)鎖,甚至同一個(gè)程序也可以獲得多個(gè)鎖,就會出現(xiàn)線程安全問題
- 需要在多個(gè)JVM之外做一個(gè)共享的 多進(jìn)程可見的 互斥的 鎖監(jiān)視器——分布式鎖
- 實(shí)現(xiàn)分布式鎖的三大方式:MySQL、Redis、Zookeeper,MySQL和Zookeeper比Redis安全性更好,Redis性能比二者更好
如何用Redis實(shí)現(xiàn)分布式鎖?
- 獲取互斥鎖:SET lock thread1 NX EX 10,NX是互斥,確保只有一個(gè)線程可以獲取到鎖,EX是設(shè)置超時(shí)時(shí)間。
- 釋放鎖:直接手動刪除。
- 死鎖問題:若獲取到鎖后線程宕機(jī),容易出現(xiàn)死鎖,應(yīng)該增加過期時(shí)間,超時(shí)自動釋放鎖。
- 誤刪問題:若線程1獲取到鎖,但業(yè)務(wù)執(zhí)行時(shí)間過長,超過了TTL,會自動釋放鎖,此時(shí)線程2嘗試獲取鎖成功,并正常執(zhí)行業(yè)務(wù),但期間線程1業(yè)務(wù)執(zhí)行完畢,正常執(zhí)行釋放鎖操作,此時(shí)就會把線程2的鎖誤刪。為了避免這種情況,應(yīng)該在獲取鎖時(shí)增加一個(gè)標(biāo)識,來表示誰占有了這個(gè)鎖,且只有它才有資格釋放鎖,因此在釋放鎖之前需要增加判斷步驟
- 基于setnx實(shí)現(xiàn)的分布式鎖存在的問題:不可重入(同一個(gè)線程無法多次獲取同一把鎖),不可重試(獲取鎖只嘗試一次,失敗不會重試),超時(shí)釋放(業(yè)務(wù)執(zhí)行耗時(shí)較長會導(dǎo)致鎖釋放,存在安全隱患)
- Redission組件:Redis基礎(chǔ)上實(shí)現(xiàn)的分布式工具集合
Redis優(yōu)化秒殺
- 優(yōu)化主要思路:將涉及到數(shù)據(jù)庫的減庫存創(chuàng)建訂單等耗時(shí)操作用異步獨(dú)立線程慢慢做,Redis只需要判斷用戶有沒有搶成功并返回結(jié)果
- 原來的秒殺流程:主要是Tomcat里面的一系列操作,有四個(gè)會直接操作數(shù)據(jù)庫,耗時(shí)非常久。相當(dāng)于一個(gè)飯店,來了一位顧客,派了一個(gè)服務(wù)員為這位顧客一條龍服務(wù),從點(diǎn)菜(查詢秒殺資格)到做飯(減庫存和創(chuàng)建訂單)都是這一個(gè)服務(wù)員做,效率非常低下。
- 優(yōu)化后的秒殺流程:在NGINX和Tomcat之家增加Redis,用于判斷該用戶能不能搶上優(yōu)惠券,并將判斷結(jié)果和優(yōu)惠券id、用戶id、訂單id一起保存到阻塞隊(duì)列,然后Tomcat從隊(duì)列中讀取消息,進(jìn)行比較耗時(shí)的減庫存和創(chuàng)建訂單操作
- 其中Redis判斷秒殺庫存的操作可以封裝到Lua腳本中執(zhí)行,以確保該操作的原子性
- 基于阻塞隊(duì)列的異步秒殺存在的問題?
- 阻塞隊(duì)列用的時(shí)JDK的,會占用JVM內(nèi)存,大量消息會造成內(nèi)存溢出
消息隊(duì)列實(shí)現(xiàn)異步秒殺
- 消息隊(duì)列:存儲管理消息
- 生產(chǎn)者:發(fā)送消息到消息隊(duì)列
- 消費(fèi)者:從消息隊(duì)列獲取消息并處理消息
- Redis實(shí)現(xiàn)消息隊(duì)列的三種方式:List、發(fā)布訂閱模式、Stream
List
- 鏈?zhǔn)降碾p端隊(duì)列,LPUSH存,RPOP取,但并沒有阻塞效果(隊(duì)列空時(shí)不會阻塞等待),BRPOP有阻塞效果。
- 優(yōu)點(diǎn):獨(dú)立于JVM存在,不占JVM內(nèi)存,不擔(dān)心上限,且可以持久化,還能保證消息有序性
- 缺點(diǎn):無法避免消息丟失,只支持一對一
發(fā)布訂閱模式
- 消費(fèi)者訂閱一個(gè)或多個(gè)channel,生產(chǎn)者向?qū)?yīng)channel發(fā)送消息
- 優(yōu)點(diǎn):支持一對多,一個(gè)生產(chǎn)者可以把消息發(fā)給多個(gè)消費(fèi)者。天生支持阻塞
- 缺點(diǎn):不支持?jǐn)?shù)據(jù)持久化,無法避免消息丟失,消息堆積有上限
Stream
- 優(yōu)點(diǎn):消息可回溯,支持一對多,支持阻塞讀取
- 缺點(diǎn):可能會漏讀消息
- 消費(fèi)者組:將多個(gè)消費(fèi)者劃分到一個(gè)組中,監(jiān)聽同一個(gè)消息隊(duì)列,那么多個(gè)消費(fèi)者就會競爭這些消息,可以加快處理消息的速度,避免消息堆積。消費(fèi)者組還會維護(hù)一個(gè)標(biāo)識,記錄最后一個(gè)被處理的消息,可以很快恢復(fù)突發(fā)情況,避免漏讀消息。此外,消費(fèi)者拿到消息后,Redis并不會直接不管這條消息,而是將消息置為pending狀態(tài),表示這條消息取上了但還沒處理完,處理完后通過XACK確認(rèn)消息,標(biāo)記為已處理,此時(shí)Redis才會放心地把消息從隊(duì)列中移除,可以防止消息丟失。
- 消費(fèi)者組優(yōu)點(diǎn):消息可回溯,可以多消費(fèi)者爭搶消息,加快消費(fèi)速度,可以阻塞讀取,不會漏讀消息,有消息確認(rèn)機(jī)制,保證消息至少被消費(fèi)一次
三種消息隊(duì)列對比總結(jié)文章來源地址http://www.zghlxwxcb.cn/news/detail-687664.html
到了這里,關(guān)于Redis項(xiàng)目實(shí)戰(zhàn)——優(yōu)惠券秒殺的文章就介紹完了。如果您還想了解更多內(nèi)容,請?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!