排他锁,请求各位大神指点

1. 运行环境#

liunx

1). 当前使用的 Laravel 版本?#

laravel9
//: <> (使用 php artisan --version 命令查看)

2). 当前使用的 php/php-fpm 版本?#

PHP 版本:
8.1
//: <> (使用 php --version 命令查看 php 版本)

1. 问题描述?#

排他锁,是否可以这样使用。

  DB::beginTransaction();
        try {
            $order = Order::LockForUpdate()->where('order_sn', 订单号)->first();
            $order->update($date);
            $order->user->LockForUpdate()->increment('sum', $this->collection->get('amount'));
            $order->user->notify(new UserOrder($order));
            $order->user->increment('notification_count');
            DB::commit();
        } catch (\Exception $exception) {
            Log::info($exception->getMessage() ?? $this->collection->get('order_sn') . '订单处理失败');
            DB::rollBack();
        }

在反响关联的时候加锁,这样是否合理。?
订单表 Order 加锁了。

Order::LockForUpdate()

修改用户表 User 时候也加锁。

$order->user->LockForUpdate()

2. 您期望得到的结果?#

希望在修改订单的时候禁止其他用户修改订单信息
同时修改用户金额的时候也禁止修改用户的金额
//: <> (能截图就截图。)

《L05 电商实战》
从零开发一个电商项目,功能包括电商后台、商品 & SKU 管理、购物车、订单管理、支付宝支付、微信支付、订单退款流程、优惠券等
《L04 微信小程序从零到发布》
从小程序个人账户申请开始,带你一步步进行开发一个微信小程序,直到提交微信控制台上线发布。
gongmeng
最佳答案
  DB::beginTransaction();
        try {
            $order = Order::LockForUpdate()->where('order_sn', 订单号)->first();
            $order->update($date);
            $order->user->LockForUpdate()->increment('sum', $this->collection->get('amount'));
            $order->user->notify(new UserOrder($order));
            $order->user->increment('notification_count');
            DB::commit();
        } catch (\Exception $exception) {
            Log::info($exception->getMessage() ?? $this->collection->get('order_sn') . '订单处理失败');
            DB::rollBack();
        }

如果只是当前事务并发的话,这段我认为,只需要第一句话加锁就行了,后续加锁意义不大,整个事务阶段由于第一段加锁,其他事务在获取第一句话的就被阻塞了。

2年前 评论
hhhhkkk 2年前
chen900804 (楼主) 2年前
hhhhkkk 2年前
gongmeng (作者) 2年前
hhhhkkk 2年前
讨论数量: 11
gongmeng
  DB::beginTransaction();
        try {
            $order = Order::LockForUpdate()->where('order_sn', 订单号)->first();
            $order->update($date);
            $order->user->LockForUpdate()->increment('sum', $this->collection->get('amount'));
            $order->user->notify(new UserOrder($order));
            $order->user->increment('notification_count');
            DB::commit();
        } catch (\Exception $exception) {
            Log::info($exception->getMessage() ?? $this->collection->get('order_sn') . '订单处理失败');
            DB::rollBack();
        }

如果只是当前事务并发的话,这段我认为,只需要第一句话加锁就行了,后续加锁意义不大,整个事务阶段由于第一段加锁,其他事务在获取第一句话的就被阻塞了。

2年前 评论
hhhhkkk 2年前
chen900804 (楼主) 2年前
hhhhkkk 2年前
gongmeng (作者) 2年前
hhhhkkk 2年前

increment 是原子的,不上锁也行,并发情况下也能保证原子性

2年前 评论
chen900804 (楼主) 2年前
ysxpark (作者) 2年前

update 本身就有排他锁

2年前 评论

我写了一个中间件服务对象,我贴部分代码吧,没办法在这里贴全

    private function _doit(BaseEvent $event): ?array
    {
        $this->_stopPropagation($event);

        $transaction = $event::transaction() === 0;
        if ($transaction) 
        {
            $transaction = (int)max(0, min(4, $this->_option[self::TRANSACTION] ?? 2));
            $event->beginTransaction($transaction);
        }

        $action = $this->_before ? ($this->_before)($event) : null;
        if ($action === false) return [null];

        $data = event($event);
        $transaction && $event->commit();

        return $data;
    }

我说下我的思路

  1. 我所有的事务都基于一个 BaseEvent

  2. BaseEvent 通过 beginTransaction 开启事务

  3. 在 event 中 t 通过 actionPush 引入 model(实际我是引入一个 service,这里可以按需求来)

      public function actionPush(?TransactionContract $server): ?TransactionContract
      {
          if (0 !== self::$_transaction && $server && !in_array($server, self::$_actionList)) 
          {
              self::$_actionList[] = $server;
              $server->mark(self::$_transaction);
          }
    
          return $server;
      }

    然后在 Controller 调用 event,所有的事务都将开启排它锁

        $event = new TaskCreateEvent($task_info, $service, $tags);
        app('Event\Middleware')->dispatch(
              $event, fn() => $this->authorize('canStore', [$event->task()])
          );

这里要说一点事,我的鉴权也是在 event 中的闭包中去执行的,这样就实现了并发同时开启排他锁鉴权是否要继续操作。

2年前 评论