• 事务
    • 开启事务
    • 事务回滚
    • 事务提交
    • 常见问题
      • 事务是否支持嵌套
      • 如果事务没有提交怎么办
    • 错误示范

    事务

    开启事务

    开启事务后,事务之间的所有操作都是同一个连接,注意不能使用并发操作。

    如果你想要开始一个事务,并且对回滚和提交能够完全控制,那么你可以使用 DB 的 beginTransaction 方法:

    1. DB::beginTransaction();

    或者

    1. DB::connection()->beginTransaction();

    一旦开启了事务,当前连接会绑定到当前的协程环境中,保证提交回滚查询都是同一个连接保证数据的安全性,只有提交或者回滚完毕才会解除绑定。

    事务回滚

    如果操作失败需要回滚 使用下面两种方式都可以

    1. DB::rollBack();

    或者

    1. DB::connection()->rollBack();

    事务提交

    如果操作执行成功需要 使用下面两种方式都可以

    1. DB::commit();

    或者

    1. DB::connection()->commit();

    常见问题

    事务是否支持嵌套

    MySQL 官方文档说 如果事务发生嵌套,会隐式提交上一个事务,然后开启一个新的事务。

    框架的答案是可以做事务嵌套,因为部分数据库支持 支持 savepoints 能力。
    在MySQL中, 保存点savepoints属于事务控制处理部分。利用savepoints可以回滚指定部分事务,从而使事务处理更加灵活和精细。如果你进行了事务嵌套嵌套的事务会保存在savepoints里面,可以做嵌套事务的精细控制。

    1. DB::beginTransaction();
    2. $user = User::find($id);
    3. $user->update(['name' => $id]);
    4. DB::beginTransaction();
    5. User::find($id)->update(['name'=>'sakuraovq']);
    6. DB::rollBack();
    7. DB::commit();

    嵌套里面的的事务进行了回滚,不会影响外层改变的数据只回滚name=sakuraovq的修改,这段代码执行下来 name = $id

    如果事务没有提交怎么办

    1. DB::connection()->beginTransaction();
    2. $user = User::find($id);
    3. \sgo(function ()use($id) {
    4. DB::connection()->beginTransaction();
    5. User::find($id);
    6. });

    类似这样的代码如果我们忘记 提交事务/回滚。

    Swoft 在SwoftEvent::COROUTINE_DEFER事件中会检查是否还处于事务状态,如果是会自动 rollback到最初开启事务的状态。连接会归还到连接池中,不会造成资源泄露。

    错误示范

    1. DB::beginTransaction();
    2. $user = User::find($id);
    3. \sgo(function () use ($id) {
    4. $user1 = User::find($id);
    5. });
    6. $user->update(['name' => 'sakuraovq'.mt_rand(110,10000)]);
    7. DB::commit();

    类似这样的代码虽然执行没有问题,这种写法是错误的,会造成数据的错乱。请不要在事务中嵌套协程,在执行 db 操作, 上文讲到事务是绑定到当前协程的 切换了协程也就是另一个新的连接。

    1. DB::connection()->beginTransaction();
    2. $user = User::find($id);
    3. $user->update(['name' => 2]);
    4. \sgo(function () use ($user) {
    5. $user->update(['name' => 1]);
    6. });
    7. DB::rollBack();

    这样也是错的,这段代码执行下来你会发现,子协程的修改操作回滚不受控制。因为它们使用了不同的连接,并非绑定在一起。