ITPub博客

首页 > 数据库 > MySQL > 实现秒杀的几个想法

实现秒杀的几个想法

原创 MySQL 作者:壹頁書 时间:2015-08-26 13:16:05 0 删除 编辑
第一种场景,秒杀单个商品,多人抢占一个商品资源,可以使用唯一索引进行控制.

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/,如需转载,请注明出处,否则将追究法律责任。

请登录后发表评论 登录
全部评论

注册时间:2013-10-19

  • 博文量
    621
  • 访问量
    5957519