實現秒殺的幾個想法

壹頁書發表於2015-08-26
第一種場景,秒殺單個商品,多人搶佔一個商品資源,可以使用唯一索引進行控制.

commodity是商品表,記錄可以秒殺的商品
commodity_log是秒殺記錄表,記錄那些使用者成功秒殺了哪些商品
實驗初始化了五個商品(id為1,2,3,4,5),並且在秒殺記錄表建立了一個唯一索引
  1. drop table commodity;
  2. drop table commodity_log;
  3. create table commodity (commodityId int,userid int);
  4. create table commodity_log (id int primary key auto_increment,commodityId int,userid int);
  5. insert into commodity(commodityId) values(1),(2),(3),(4),(5);
  6. create unique index inx_1 on commodity_log(commodityId);

假設20號使用者秒殺1號商品,大致過程如下
start transaction;
insert into commodity_log(commodityId,userid) values(1,20);
如果沒有異常
    update commodity set userid=20 where commodityId=1;
    commit;
如果有異常,表示秒殺失敗
    rollback;

這種方式的優點是實現簡單,缺點是在併發環境下,有阻塞.

第二種場景,秒殺的每種商品,有不同的剩餘數量.
commodity表的Quantity欄位,表示本次秒殺的商品總數
commodity_log表的state欄位,表示是否秒殺成功,0表示不成功,1表示成功.

  1. drop table commodity;
  2. drop table commodity_log;
  3. create table commodity (commodityId int,Quantity int);
  4. create table commodity_log (id int primary key auto_increment,commodityId int,userid int,state int default 0);
  5. insert into commodity(commodityId,Quantity) values(1,5),(2,5),(3,3),(4,1),(5,2);

主要邏輯如下
  1. drop procedure buy;

  2. delimiter $$
  3. create procedure buy(v_commodityId int,v_userid int,out result varchar(20))
  4. begin
  5.     declare flag int;
  6.     declare v_log_id int;
  7.     
  8.     start transaction;
  9.     select if(count(*)<
  10.     (select max(quantity) from commodity a where a.commodityId=commodityId),true,false) into flag
  11.     from commodity_log where commodityId=v_commodityId;
  12.     if flag=true then
  13.         insert into commodity_log(commodityId,userid) values(v_commodityId,v_userid);
  14.         select id into v_log_id from (
  15.             select id,userid,@a:=@a+1 rn from commodity_log,(select @a:=0) t where commodityId=v_commodityId order by id
  16.         ) a where rn <=(select max(quantity) from commodity a where a.commodityId=v_commodityId) and userid=v_userid;
  17.         if v_log_id is not null then
  18.             update commodity_log set state=1 where id=v_log_id;
  19.             set result='成功秒殺';
  20.         else
  21.             set result='秒殺失敗';
  22.         end if;
  23.     else
  24.         set result='秒殺失敗';
  25.     end if;
  26.     commit;
  27. end $$
  28. delimiter ;
select if(count(*)<
(select max(quantity) from commodity a where a.commodityId=commodityId),true,false) into flag
from commodity_log where commodityId=v_commodityId;

這段進行一個基本判斷,如果秒殺記錄表的記錄已經超過了本次秒殺的商品數量,則直接退出.
如果沒有超過本次的秒殺商品數量,則記錄一個秒殺日誌
在併發的環境下,因為沒有加鎖的併發控制,插入的日誌記錄一定大於商品數量
所以按照commodity_log日誌表的自增ID作為行為先後的判斷依據.
如果秒殺成功,則回寫狀態.

這種方式沒有鎖和阻塞.但是sql語句大幅增加
秒殺不成功有兩種場景.第一種會有一個select語句,第二種兩個select語句和一個insert語句
秒殺成功,兩個select語句,一個insert語句和一個update語句。

這種方案雖然避免了秒殺中使用鎖和阻塞,但是增加了很多sql語句.

哪種更好呢?
我也不太清楚.

後記:
優化版
  1. drop procedure buy;

  2. delimiter $$
  3. create procedure buy(v_commodityId int,v_userid int,out result varchar(20))
  4. begin
  5.     declare flag int;
  6.     declare v_log_id int;
  7.     declare v_quantity int;
  8.     declare v_id int;
  9.     
  10.     start transaction;
  11.     select quantity into v_quantity from commodity a where a.commodityId = v_commodityId;

  12.     select if(count(*)<v_quantity,true,false) into flag
  13.     from commodity_log where commodityId=v_commodityId;

  14.     if flag=true then
  15.         insert into commodity_log(commodityId,userid) values(v_commodityId,v_userid);
  16.         SELECT LAST_INSERT_ID() into v_id;
  17.         
  18.         select id into v_log_id from (
  19.             select id,@a:=@a+1 rn from commodity_log,(select @a:=0) t where commodityId=v_commodityId order by id
  20.         ) a where rn <=v_quantity and id=v_id;
  21.         
  22.         if v_log_id is not null then
  23.             update commodity_log set state=1 where id=v_log_id;
  24.             set result='成功秒殺';
  25.         else
  26.             set result='秒殺失敗';
  27.         end if;
  28.     else
  29.         set result='秒殺失敗';
  30.     end if;
  31. end $$
  32. delimiter ;
過程中移除了commit,轉到程式中控制.

參考:
http://jiagou.baijia.baidu.com/article/108134?qq-pf-to=pcqq.group

http://www.infoq.com/cn/presentations/seckill-solution-based-sql



來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/29254281/viewspace-1783043/,如需轉載,請註明出處,否則將追究法律責任。

相關文章