商品秒杀问题解决方案

2018-06-11  本文已影响0人  Stargazes

一、悲观锁

    悲观锁的方案采用的是排他读,同时只有一个进程能够操作当前的记录,事务在提交或回滚之后,锁会释放,其他的进程才能读取,在性能要求不严格的情况下,可直接使用改方案。要注意的是 SELECT … FOR UPDATE要尽可能的使用索引,以便锁定尽可能少的行数;排他锁是在事务执行结束之后才释放的,不是读取完成之后就释放,因此使用的事务应该尽可能的早些提交或回滚,以便早些释放排它锁。

//0.开始事务 begin;/begin work;/start transaction; (三者选一就可以)

//1.查询出商品信息 select status from t_goods where id=1 for update;

//2.根据商品信息生成订单 insert into t_orders (id,goods_id) values (null,1);

//3.修改商品status为2 update t_goods set status=2;

//4.提交事务 commit;/commit work;

二、乐观锁

    乐观锁的方案在读取数据是并没有加排他锁,而是通过一个每次更新都会自增的version字段来解决,多个进程读取到相同num,然后都能更新成功的问题。在每个进程读取num的同时,也读取version的值,并且在更新num的同时也更新version,并在更新时加上对version的等值判断。假设有10个进程都读取到了num的值为1,version值为9,则这10个进程执行的更新语句都是UPDATE goods SET num=num-1,version=version+1 WHERE version=9,然而当其中一个进程执行成功之后,数据库中version的值就会变为10,剩余的9个进程都不会执行成功,这样保证了商品不会超发,num的值不会小于0,但这也导致了一个问题,那就是发出抢购请求较早的用户可能抢不到,反而被后来的请求抢到了。

1.查询出商品信息 select (status,status,version) from t_goods where id=#{id}

2.根据商品信息生成订单

3.修改商品status为2

update t_goods set status=2,version=version+1 where id=#{id} and version=#{version};

三、where条件下的原子操作

    每次对数量进行update操作时增加where数目大于0的条件,例如num为5,只要num>0 都能执行update语句

四、redis watch方案

       Redis Watch 命令用于监视一个(或多个) key ,如果在事务执行之前这个(或这些) key 被其他命令所改动,那么事务将被打断。

类似mysql的乐观锁,客多个进程读取同一条记录,但是只能有一个进程事务执行成功,后面的执行都被中断。

$num = $this->redis->get('num');

$this->redis->watch('num');//watch相应的key值,

$res = $this->redis->multi()->decr('num')->lPush('result',$num)->exec(); //执行key值的修改事务,decr($num),返回减一之后的数值

五、redis list队列方案

    将商品信息保存在消息队列中,利用队列弹出操作的原子性,抢购时依次从list中pop出商品,不存在商品超卖的情况,缺点是商品多了话要把所有的商品信息存到内存上。

六、基于redis decr返回值的方式,

    类似mysql的where操作。

上一篇下一篇

猜你喜欢

热点阅读