用php写京东抢购,关于抢京东券高并发的问题?
之前在一個微信公眾號上做了一個搶京東券的功能, 50 張京東券,面額 50 、 100 不等,存在一張 card 表中,四個字段, id , number , money , is_taken 。
因為之前沒有這種高并發處理的經驗,所以使用了一種最傳統的方式來實現:
方案一:來一個人我就從數據庫中取一張京東券出來給他,并將該京東券標記為已使用(即更新 is_taken 字段),并將該用戶插入到 winner 表中
這個方案最終導致的悲劇是,有一張京東券被兩個人領取到了。
我所理解會出現這個問題的原因是獲取未被占用的京東券數據( select )和更新該條京東券數據( update )是兩個獨立的操作,在這兩個操作之間存在時間間隔,例如 A 用戶剛得到了一張 100 元的京東券,還未來得及更新, B 用戶涌入查詢到這張京東券未被使用,所以 B 用戶也獲得了這張京東券。
問題一:我這個理解是正確的嗎?還是有更深入的原因?
出了這個問題后,我在網上查找關于高并發相關的資料,幾乎都提到了隊列和鎖。就我個人了解隊列可以使用 redis 或者 memcacheq(這個沒用過,不熟悉),所以自己想了第二種方案。
方案二:事先將京東券 id 數據壓入到 redis 的 list 中,每過來一個有效的用戶,就 pop 一個 id 給他(當 pop 出來的數據為空時說明京東券已經被搶光),并將用戶 id 與京東券 id 的對應關系存儲到 redis 的 set 當中去,然后根據這個 id 來查找京東券數據,顯示給用戶京東券的面額,并將 set 中的數據存儲到數據庫當中去。
個人覺得這種方案會比第一種方案要好的多。但是沒有真正意義上去實踐過,只是個人思考的一個結果。
問題二:第二種方案是否可行?是否還有更優方案?或者說方案二是否有可以優化的地方?
問題三:在高并發時很多文章中說到的鎖是一個怎樣的概念呢?我的理解是這個鎖就像是數據庫的一個大門,一次只放一個人進去,是這樣嗎?具體該如何設計和使用?
問題四:在應對大流量高并發的情況時,在服務器層面要做哪些工作?
問題五:我所舉得這個例子與平常類似網上商城中的秒殺功能有哪些相同和相異之處呢?是否可以按照方案二的設計思路進行設計呢?
回復內容:
之前在一個微信公眾號上做了一個搶京東券的功能, 50 張京東券,面額 50 、 100 不等,存在一張 card 表中,四個字段, id , number , money , is_taken 。
因為之前沒有這種高并發處理的經驗,所以使用了一種最傳統的方式來實現:
方案一:來一個人我就從數據庫中取一張京東券出來給他,并將該京東券標記為已使用(即更新 is_taken 字段),并將該用戶插入到 winner 表中
這個方案最終導致的悲劇是,有一張京東券被兩個人領取到了。
我所理解會出現這個問題的原因是獲取未被占用的京東券數據( select )和更新該條京東券數據( update )是兩個獨立的操作,在這兩個操作之間存在時間間隔,例如 A 用戶剛得到了一張 100 元的京東券,還未來得及更新, B 用戶涌入查詢到這張京東券未被使用,所以 B 用戶也獲得了這張京東券。
問題一:我這個理解是正確的嗎?還是有更深入的原因?
出了這個問題后,我在網上查找關于高并發相關的資料,幾乎都提到了隊列和鎖。就我個人了解隊列可以使用 redis 或者 memcacheq(這個沒用過,不熟悉),所以自己想了第二種方案。
方案二:事先將京東券 id 數據壓入到 redis 的 list 中,每過來一個有效的用戶,就 pop 一個 id 給他(當 pop 出來的數據為空時說明京東券已經被搶光),并將用戶 id 與京東券 id 的對應關系存儲到 redis 的 set 當中去,然后根據這個 id 來查找京東券數據,顯示給用戶京東券的面額,并將 set 中的數據存儲到數據庫當中去。
個人覺得這種方案會比第一種方案要好的多。但是沒有真正意義上去實踐過,只是個人思考的一個結果。
問題二:第二種方案是否可行?是否還有更優方案?或者說方案二是否有可以優化的地方?
問題三:在高并發時很多文章中說到的鎖是一個怎樣的概念呢?我的理解是這個鎖就像是數據庫的一個大門,一次只放一個人進去,是這樣嗎?具體該如何設計和使用?
問題四:在應對大流量高并發的情況時,在服務器層面要做哪些工作?
問題五:我所舉得這個例子與平常類似網上商城中的秒殺功能有哪些相同和相異之處呢?是否可以按照方案二的設計思路進行設計呢?
我估計你的方案一是這樣子的
select * from card where is_token=0 limit 1;
//有結果時,繼續執行以下語句
update card set is_token=1 where id=xx;
insert into winner ...
改進
select * from card where is_token=0 limit 1;
//有結果時,繼續執行以下語句
update card set is_token=1 where id=xx and is_token=0;
//獲取影響行數,為0則失敗,否則執行 insert into winner ...
方案二
可行。
因為你的業務邏輯比較簡單,也可以使用redis的計數器
即當計數器 <=50 時按順序一人領一張,通過計數器來過濾
問題三 鎖
悲觀鎖(Pessimistic Lock)
顧名思義,就是很悲觀,每次去拿數據的時候都認為別人會修改,所以每次在拿數據的時候都會上鎖,這樣別人想拿這個數據就會block直到它拿到鎖。傳統的關系型數據庫里邊就用到了很多這種鎖機制,比如行鎖,表鎖等,讀鎖,寫鎖等,都是在做操作之前先上鎖。
begin;
select * from card where is_token=0 limit 1 for update;
樂觀鎖
顧名思義,就是很樂觀,每次去拿數據的時候都認為別人不會修改,所以不會上鎖,但是在更新的時候會判斷一下在此期間別人有沒有去更新這個數據,可以使用版本號等機制。參考改進后的方案一。
樂觀鎖用的比較多,使用redis的watch
本文原創發布php中文網,轉載請注明出處,感謝您的尊重!
總結
以上是生活随笔為你收集整理的用php写京东抢购,关于抢京东券高并发的问题?的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: django如何调用php接口,使用dj
- 下一篇: 动态规划算法php,php算法学习之动态