使用Redis实现乐观锁

事务概念

事务是一个单独的隔离操作:事务中的所有命令都会序列化、按顺序地执行。事务在执行的过程中,不会被其他客户端发送来的命令请求所打断。

事务是一个原子操作:事务中的命令要么全部被执行,要么全部都不执行。这点和mysql的事务不太一样,在mysql事务开启后,当操作中存在异常,则会导致事务回滚,不会出现数据异常,但是对于redis而言,当事务执行过程中,出现异常,redis会

记录异常,但是操作会继续执行。

redis事务是一组命令的集合。多组命令进入到等待执行的事务队列中,执行exec命令告诉redis将等待执行的事务队列中的所有命令,按顺序执行,返回值就是这些命令组成的列表。

Redis 事务可以一次执行多个命令, 具有下列保证:

  • 批量操作在发送 EXEC 命令前被放入队列缓存。
  • 收到 EXEC 命令后进入事务执行,事务中任意命令执行失败,其余的命令依然被执行。
  • 在事务执行过程,其他客户端提交的命令请求不会插入到事务执行命令序列中。

一个事务从开始到执行会经历以下三个阶段:

  • 开始事务。
  • 命令入队。
  • 执行事务。

事务中的错误:

  • 事务在执行 EXEC 之前,入队的命令可能会出错。比如说,命令可能会产生语法错误(参数数量错误,参数名错误,等等),或者其他更严重的错误,比如内存不足(如果服务器使用 maxmemory 设置了最大内存限制的话)。
  • 命令可能在 EXEC 调用之后失败。举个例子,事务中的命令可能处理了错误类型的键,比如将列表命令用在了字符串键上面,诸如此类。

服务器会对命令入队失败的情况进行记录,并在客户端调用 EXEC 命令时,拒绝执行并自动放弃这个事务

例如:

在 EXEC 命令执行之后所产生的错误, 并没有对它们进行特别处理: 即使事务中有某个/某些命令在执行时产生了错误, 事务中的其他命令仍然会继续执行

例如:

 

redis 事务入队只会检查语法错误,对于exec后执行错误,没有回滚措施。而且在事务中无法在客户端做查询判断,只会得到queued,无法进行业务数据判断,也是很坑。

redis的事务命令:

multi 开启事务

exec 执行

discard 取消事务

watch key 监视key

unwatch key取消监视

乐观锁

乐观的认为数据不会出现冲突,使用version或timestamp来记录判断。乐观锁的优点开销小,不会出现锁冲突。

可利用watch命令监听key,实现乐观锁,来保证不会出现冲突,应用场景比如秒杀来防止超卖。

伪代码如下:

实际代码如下:

        //商品总量
        $shop_account = $request->input('number');
        //商品总库存的键
        $products = 'products';

        //判断商品是否存在
        if(!Redis::exists($products)){
            return $this->failed('购买的商品已下架');
        }

        //判断商品是否还有库存
        if(Redis::get($products) == 0){
            return $this->failed('商品暂无库存,请联系店家上货');
        }

        //判断购买的商品数量是否大于库存数量
        if(Redis::get($products) < $shop_account){
            return $this->failed('购买商品超出库存总量');
        }

        //开始购买
        try{
            //监视库存总量
            Redis::watch($products);

            //开启事务
            Redis::multi();

            //购买后减少库存
            Redis::decrby($products,$shop_account);
            //下面肯定会有一些其余的操作...

            //结束,并执行事务
            $res = Redis::exec();

            //判断$products在事务执行过程中,是否被更改了

            if ($res === false){
                return $this->failed('店家更改了商品库存总量,请重新提交');
            }

            return $this->message('购买成功,感谢您的支持');

        }catch (\Exception $exception){
            //假如在事务入列中,存在异常,则取消事务
            Redis::discard();
        }

  

posted @ 2021-01-04 18:11  易文杰  阅读(426)  评论(0编辑  收藏  举报