最近维护历史代码,使用的是phalapi 最初版本开发,数据库操作使用的是notorm。notorm本身不支持事务嵌套,但是在开发过程中,多个操作进行拆分,根据不同业务不同进行调用,必然会设计到多个事务嵌套在一起的问题。举个栗子:
1) 公共模块A,更新用户的账户余额,添加流水记录操作。
2) 模块B,根据用户的操作(消费或充值)根据活动配置赠送相应的优惠券。
基础业务A模块就够用了,但是出现一些业务活动的时候,需要在A成功之后调用B模块,只有两个操作成功之后才完整提交事务。要实现这样的功能,无非两种方式:
1) 模块内部不加事务,事务控制统一交给调用方。谁调用,谁负责事务。内部模块只提供内部模块执行结果。
2) 模块内部控制事务,外部调用只需知道内部执行是否成功。
如果设计的合理,还是比较倾向于使用第一种方案。但是不可否认,你无法确定你的调用模块会不会再被其他人调用,最终结果又演变成第二种方案。因此底层还是需要支持事务嵌套。
嵌套事务的核心思想就是添加一个计数器,第一次开启事务,最后一次提交或回滚执行数据库操作,其他情况只是更新计数器数值。分别看一看几个现有框架如何设计数据库事务嵌套操作:
1) Laravel
Laravel与事务相关操作封装在 Illuminate\Database\Concerns\ManagesTransactions当中。
2) Yii2
Yii2的事务操作也是有一个单独文件进行封装,yii\db\Transaction。
3) Thinkphp5
Thinkphp5的事务操作并没有独立分开,直接在数据库连接类当中think\db\Connection;
三个框架都是通过计数器以及数据库本身的"部分事务"支持嵌套事务的操作。MYSQL 中通过 savepoint 的方式来实现只提交事务的一部分。操作流程大体分一下三步
1) 开启事务,检查计数器是否是第一次开启,如果是则执行pdo开启事务,不是则修改计数器的值,同时根据是否支持部分事务,执行pdo savepoint操作。
2) 事务提交,检查计数器是否是最外层事务,是则执行pdo事务提交操作,否则计数器减1
3) 事务回滚,检查计算器是否是最外层操作,是则执行pdo事务回滚,否则计数器减1,同时根据是否支持部分事务,执行pdo rollbak to savepoint 操作
虽然整体思路一样,但是三个框架根据自身的特定,代码设计抽象程度不一样。从这个相同的功能,也能够很好的体会三个框架不同的设计方式。
领取专属 10元无门槛券
私享最新 技术干货