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

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. 您期望得到的结果?

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

《L03 构架 API 服务器》
你将学到如 RESTFul 设计风格、PostMan 的使用、OAuth 流程,JWT 概念及使用 和 API 开发相关的进阶知识。
《L02 从零构建论坛系统》
以构建论坛项目 LaraBBS 为线索,展开对 Laravel 框架的全面学习。应用程序架构思路贴近 Laravel 框架的设计哲学。
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();
        }

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

1年前 评论
hhhhkkk 1年前
chen900804 (楼主) 1年前
hhhhkkk 1年前
gongmeng (作者) 1年前
hhhhkkk 1年前
讨论数量: 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();
        }

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

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

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

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

update本身就有排他锁

1年前 评论

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

    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中的闭包中去执行的,这样就实现了并发同时开启排他锁鉴权是否要继续操作。

1年前 评论

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!