• 使用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();
            }
    

      

  • 相关阅读:
    表的创建与管理
    以传值和传引用的方式传递参数 IN OUT NOCOPY
    PLSQL中的三种参数模式IN、OUT、IN OUT
    用python写GPU上的并行计算程序,有什么库或者编译器?
    cupy中tensor数据类型与numpy以及pytorch中相互转换
    c++ string split
    Java 读取大文件
    Linux 使用系列
    安装以太坊环境
    服务器排查问题相关命令
  • 原文地址:https://www.cnblogs.com/ywjcqq/p/14231320.html
Copyright © 2020-2023  润新知