1. 程式人生 > >Redis 秒殺

Redis 秒殺

搶購、秒殺是如今很常見的一個應用場景,主要需要解決的問題有兩個:
1 高併發對資料庫產生的壓力
2 競爭狀態下如何解決庫存的正確減少("超賣"問題)
對於第一個問題,已經很容易想到用快取來處理搶購,避免直接操作資料庫,例如使用Redis。
重點在於第二個問題

常規寫法:

查詢出對應商品的庫存,看是否大於0,然後執行生成訂單等操作,但是在判斷庫存是否大於0處,如果在高併發下就會有問題,導致庫存量出現負數

[php]  view plain  copy
  1. <?php  
  2. $conn=mysql_connect("localhost","big","123456");    
  3. if(!$conn){    
  4.     echo "connect failed";    
  5.     exit;    
  6. }   
  7. mysql_select_db("big"
    ,$conn);   
  8. mysql_query("set names utf8");  
  9.   
  10. $price=10;  
  11. $user_id=1;  
  12. $goods_id=1;  
  13. $sku_id=11;  
  14. $number=1;  
  15.   
  16. //生成唯一訂單  
  17. function build_order_no(){  
  18.     return date('ymd').substr(implode(NULL, array_map('ord'str_split(substr(uniqid(), 7, 13), 1))), 0, 8);  
  19. }  
  20. //記錄日誌  
  21. function insertLog($event,$type=0){  
  22.     global $conn;  
  23.     $sql="insert into ih_log(event,type)   
  24.     values('$event','$type')";    
  25.     mysql_query($sql,$conn);    
  26. }  
  27.   
  28. //模擬下單操作  
  29. //庫存是否大於0  
  30. $sql="select number from ih_store where goods_id='$goods_id' and sku_id='$sku_id'";//解鎖 此時ih_store資料中goods_id='$goods_id' and sku_id='$sku_id' 的資料被鎖住(注3),其它事務必須等待此次事務 提交後才能執行  
  31. $rs=mysql_query($sql,$conn);  
  32. $row=mysql_fetch_assoc($rs);  
  33. if($row['number']>0){//高併發下會導致超賣  
  34.     $order_sn=build_order_no();  
  35.     //生成訂單    
  36.     $sql="insert into ih_order(order_sn,user_id,goods_id,sku_id,price)   
  37.     values('$order_sn','$user_id','$goods_id','$sku_id','$price')";    
  38.     $order_rs=mysql_query($sql,$conn);   
  39.       
  40.     //庫存減少  
  41.     $sql="update ih_store set number=number-{$number} where sku_id='$sku_id'";  
  42.     $store_rs=mysql_query($sql,$conn);    
  43.     if(mysql_affected_rows()){    
  44.         insertLog('庫存減少成功');  
  45.     }else{    
  46.         insertLog('庫存減少失敗');  
  47.     }   
  48. }else{  
  49.     insertLog('庫存不夠');  
  50. }  
  51. ?>  

優化方案1:將庫存欄位number欄位設為unsigned,當庫存為0時,因為欄位不能為負數,將會返回false

[php]  view plain  copy
  1. //庫存減少  
  2. $sql="update ih_store set number=number-{$number} where sku_id='$sku_id' and number>0";  
  3. $store_rs=mysql_query($sql,$conn);    
  4. if(mysql_affected_rows()){    
  5.     insertLog('庫存減少成功');  
  6. }  

優化方案2:使用mysql的事務,鎖住操作的行

[php]  view plain  copy
  1. <?php  
  2. $conn=mysql_connect("localhost","big","123456");    
  3. if(!$conn){    
  4.     echo "connect failed";    
  5.     exit;    
  6. }   
  7. mysql_select_db("big",$conn);   
  8. mysql_query("set names utf8");  
  9.   
  10. $price=10;  
  11. $user_id=1;  
  12. $goods_id=1;  
  13. $sku_id=11;  
  14. $number=1;  
  15.   
  16. //生成唯一訂單號  
  17. function build_order_no(){  
  18.     return date('ymd').substr(implode(NULL, array_map('ord'str_split(substr(uniqid(), 7, 13), 1))), 0, 8);  
  19. }  
  20. //記錄日誌  
  21. function insertLog($event,$type=0){  
  22.     global $conn;  
  23.     $sql="insert into ih_log(event,type)   
  24.     values('$event','$type')";    
  25.     mysql_query($sql,$conn);    
  26. }  
  27.   
  28. //模擬下單操作  
  29. //庫存是否大於0  
  30. mysql_query("BEGIN");   //開始事務  
  31. $sql="select number from ih_store where goods_id='$goods_id' and sku_id='$sku_id' FOR UPDATE";//此時這條記錄被鎖住,其它事務必須等待此次事務提交後才能執行  
  32. $rs=mysql_query($sql,$conn);  
  33. $row=mysql_fetch_assoc($rs);  
  34. if($row['number']>0){  
  35.     //生成訂單   
  36.     $order_sn=build_order_no();   
  37.     $sql="insert into ih_order(order_sn,user_id,goods_id,sku_id,price)   
  38.     values('$order_sn','$user_id','$goods_id','$sku_id','$price')";    
  39.     $order_rs=mysql_query($sql,$conn);   
  40.       
  41.     //庫存減少  
  42.     $sql="update ih_store set number=number-{$number} where sku_id='$sku_id'";  
  43.     $store_rs=mysql_query($sql,$conn);    
  44.     if(mysql_affected_rows()){    
  45.         insertLog('庫存減少成功');  
  46.         mysql_query("COMMIT");//事務提交即解鎖  
  47.     }else{    
  48.         insertLog('庫存減少失敗');  
  49.     }  
  50. }else{  
  51.     insertLog('庫存不夠');  
  52.     mysql_query("ROLLBACK");  
  53. }  
  54. ?>  


優化方案3:使用非阻塞的檔案排他鎖

[php]  view plain  copy

    相關推薦

    redis簡單實現

    redis中的list型別是很好的一個佇列,可以在秒殺的高併發中暫存快取,然後過了秒殺峰期再去插入資料庫,可以減輕伺服器很大的壓力。 基本思路: 先要做一個token防止表單重複提交,這裡用session存一下token,然後前端先請求token的介面把token存到hidden的input

    php結合redis

    public function _initialize(){         parent::_initialize();         $goods_id = I("goods_id",'0','intval');               if($goods_id){

    spring boot + Mybatis + redis 系統

    最近開了一些高併發的東西,以及一些秒殺系統,但感覺都沒有完整的描述。於是自己就動手實現了一個簡單版本的搶購系統。 本系統採用spring boot + mybatis + redis實現。 專案結構圖如下: 專案工程已放到GitHub上了,https://git

    redis系統資料同步(保證不多賣)

    秒殺系統需要保證東西不多賣,關鍵是在多個客戶端對庫存進行減操作時,必須加鎖。Redis中的Watch剛好可以實現一點。首先我們需要獲取當前庫存,只有庫存中的食物小於購物車的數目才能對庫存進行減。在高併發的情況下會出現某時刻查詢庫存夠的,但下一時刻另外一個執行緒下單了

    位元組跳動Java後臺研發工程師3面:事務+List集合+慢查詢SQL+Redis+設計

    一面 1.講講jvm執行時資料庫區 2.講講你知道的垃圾回收演算法 3.jvm記憶體模型jmm 4.記憶體洩漏與記憶體溢位的區別 5. select、epool 的區別?底層的資料結構是什麼? 6.mysql資料庫預設儲存引擎,有什麼優點 7.優化資料庫的方法,從sql到

    Redis

    搶購、秒殺是如今很常見的一個應用場景,主要需要解決的問題有兩個: 1 高併發對資料庫產生的壓力 2 競爭狀態下如何解決庫存的正確減少("超賣"問題) 對於第一個問題,已經很容易想到用快取來處理搶購,避免直接操作資料庫,例如使用Redis。 重點在於第二個問題 常規寫

    使用redis出現產品超發現象求解?

    最近在做一個秒殺活動,處於效能和響應速度的考慮,使用了redis。寫的時候就特別注意了杜絕超發現象,基於redis理論的cas(check and set)樂觀鎖,想著應該能夠杜絕該問題,但是還是出現了,很疑惑求大神幫助,具體的程式碼大致如下: <?php

    redis

    模擬 實現 lpush 高並發 假設 多線程 並發 一個用戶 用戶 redis秒殺 Redis原子性原理 摘要: 1、Redis是單進程單線程的網絡模型,用的是epoll,poll,select網絡模型,這些網絡模型都是單線程處理網絡請求 2、Re

    重學 Java 設計模式:實戰享元模式「基於Redis,提供活動與庫存資訊查詢場景」

    ![](https://img-blog.csdnimg.cn/20200614192426490.png) 作者:小傅哥 部落格:[https://bugstack.cn](https://bugstack.cn) >沉澱、分享、成長,讓自己和他人都能有所收穫!

    Redis系統架構設計-微信搶紅包

    導讀   前二天我寫了一篇,Redis高階專案實戰(點我直達),SpringBoot整合Redis附原始碼(點我直達),今天我們來做一下Redis秒殺系統的設計。當然啦,Redis基礎知識還不過關的,先去加強下自身內功,然後在回來看這篇,Redis基礎知識(點我直達)。為啥寫這個微信搶紅包專案呢,公司0202

    Redis實戰-微信搶紅包-庫存,附案例原始碼(Jmeter壓測)

    導讀   前二天我寫了一篇,Redis高階專案實戰(點我直達),SpringBoot整合Redis附原始碼(點我直達),今天我們來做一下Redis秒殺系統的設計。當然啦,Redis基礎知識還不過關的,先去加強下自身內功,然後在回來看這篇,Redis基礎知識(點我直達)。為啥寫這個微信搶紅包專案呢,公司0202

    Redis輕松實現系統

    tar 幫助 說過 腳本 .net 所有 paxos 你會 用戶 秒殺系統的架構設計 秒殺系統,是典型的短時大量突發訪問類問題。對這類問題,有三種優化性能的思路: 寫入內存而不是寫入硬盤 異步處理而不是同步處理 分布式處理 用上這三招,不論秒殺時負載多大,都能輕松應對。

    php結合redis實現高並發下的搶購、功能

    緩存 使用 fclose rtl global 簡單模擬 解決 fun 非阻塞 搶購、秒殺是如今很常見的一個應用場景,主要需要解決的問題有兩個: 1 高並發對數據庫產生的壓力 2 競爭狀態下如何解決庫存的正確減少("超賣"問題) 對於第一個問題,已經很容易想到用緩存來處理搶

    使用redis實現簡單的

    lec connect use decode else 隊列 def 固定 urn 自己做的簡單秒殺 感覺思路是沒太大問題的 但是代碼寫的不是很好 做個記錄方便以後回來嘲諷下自己 <?phpnamespace frontend\controllers;use Y

    php+redis實現電商功能

    str 數組 ash 參數設置 *** this 百萬 對數 現在 這一次總結和分享用Redis實現分布式鎖來完成電商的秒殺功能。先扯點個人觀點,之前我看了一篇博文說博客園的文章大部分都是分享代碼,博文裏強調說分享思路比分享代碼更重要(貌似大概是這個意思,若有誤請諒解),

    Redis案例——商品,購物車

    bsp ota get 秒殺 logs list edi use all 秒殺案例: 1 <?php 2 header("content-type:text/html;charset=utf-8"); 3 $redis = ne

    redis樂觀鎖(適用於系統)

    修改 導致 代碼 -a 通知 解決 redis服務器 font 變化 redis事務中的WATCH命令和基於CAS的樂觀鎖 在Redis的事務中,WATCH命令可用於提供CAS(check-and-set)功能。假設我們通過WATCH命令在事務執行之前監控了多個Keys,

    redis使用watch搶購思路

    fixed shutdown author pub 邏輯 uid trace sets ren 1、使用watch,采用樂觀鎖 2、不使用悲觀鎖,因為等待時間非常長,響應慢 3、不使用隊列,因為並發量會讓隊列內存瞬間升高 測試代碼: import java.util.co

    借助Redis和限流的思考

    rlock 會有 如果 小數據 更多 準備 while col x86 最近群裏聊起秒殺和限流,我自己沒有做過類似應用,但是工作中遇到過更大的數據和並發。 於是提出了一個簡單的模型: var count = rds.inc(key); if(count > 10

    利用redis進行商品

    開始 alt user 返回 num 關於 技術分享 線程 signed 這裏是用redis的list集合開發,redis的list集合是具有原子性的,不必擔心多線程時會取到重復的數據,即使請求同時到達也會排隊進行數據操作 1. 先說說大概思路,關於數據庫庫存字段的設計.數