在Laravel项目开发过程中,我们经常会遇到需要开启数据库事务执行业务逻辑的场景,同时业务里也需要读取Config配置来获取系统参数。但不少开发者发现,在事务执行过程中读取的配置值和预期不符,甚至出现配置错乱的情况,这其实和事务的隔离特性以及Laravel Config配置的缓存机制有关。

问题产生的原因
首先要明确两个核心机制:
- Laravel的
Config门面读取的配置,默认是加载到内存中的全局配置数组,第一次读取后会缓存该值,后续读取直接从内存获取,不会重复解析配置文件。 - 数据库事务具备隔离性,不同的事务之间、事务和外部非事务操作之间的数据可见性是有规则的,如果在事务中修改了配置相关的存储值(比如存在数据库中的配置项),直接读取
Config可能拿不到最新值。
举个例子,假设系统配置存在config/app.php中,同时有一份相同的配置存在数据库的配置表,我们启动时把数据库配置同步到Config中,此时如果在事务里更新了配置表的记录,再读取Config拿到的还是旧值,因为内存里的配置没有被更新。
安全使用的方法
方法一:事务内避免读取动态配置
如果配置是静态的,不会在事务执行过程中被修改,那么直接在事务里读取Config是安全的。比如读取系统的默认分页数量、默认时区这类不会在业务中动态变更的配置,不需要额外处理。
<?php
use IlluminateSupportFacadesConfig;
use IlluminateSupportFacadesDB;
// 静态配置读取,事务内使用无问题
DB::transaction(function () {
$pageSize = Config::get('app.page_size', 15);
// 使用$pageSize执行业务逻辑
});
方法二:事务内直接读取最新配置源
如果配置是动态的,可能在事务中被修改,那么不要依赖Config的缓存值,而是直接读取配置的原始存储源。比如配置存在数据库的配置表,事务内直接查询配置表获取最新值。
<?php
use IlluminateSupportFacadesDB;
DB::transaction(function () {
// 直接查询配置表获取最新值,不依赖Config缓存
$configValue = DB::table('system_config')
->where('key', 'order_expire_minutes')
->value('value');
// 使用$configValue执行业务逻辑
});
方法三:事务提交后更新Config配置
如果配置修改后需要同步到Config中供后续逻辑使用,那么应该在事务提交成功之后,再更新Config的内存缓存,避免事务回滚后配置被错误修改。
<?php
use IlluminateSupportFacadesConfig;
use IlluminateSupportFacadesDB;
DB::transaction(function () {
// 事务内修改配置表
DB::table('system_config')
->where('key', 'upload_max_size')
->update(['value' => 2048]);
});
// 事务提交成功后再更新Config缓存
$newValue = DB::table('system_config')
->where('key', 'upload_max_size')
->value('value');
Config::set('app.upload_max_size', $newValue);
方法四:使用闭包隔离配置读取
如果需要在事务内临时读取最新的配置值,并且不影响全局的Config缓存,可以使用闭包临时获取配置,不修改全局配置数组。
<?php
use IlluminateSupportFacadesConfig;
use IlluminateSupportFacadesDB;
DB::transaction(function () {
// 临时获取配置值,不修改全局Config
$getConfig = function ($key, $default = null) {
// 这里可以根据配置存储源动态获取,比如先查数据库再查Config
$dbValue = DB::table('system_config')
->where('key', $key)
->value('value');
return $dbValue ?? Config::get($key, $default);
};
$configVal = $getConfig('sms_template_id', '');
// 使用$configVal执行业务逻辑
});
注意事项
- 不要在事务中调用
Config::set修改全局配置,因为事务如果回滚,配置的修改不会回滚,会导致配置值和预期不一致。 - 如果配置是存在缓存中的,事务内读取缓存配置时也要注意缓存的更新时机,避免读取到旧值。
- 对于高频读取的动态配置,可以考虑在事务外提前读取并传入事务闭包,减少事务内的IO操作。
| 使用场景 | 推荐方案 |
|---|---|
| 静态配置,不会在事务中修改 | 直接读取Config配置 |
| 动态配置,事务中可能修改 | 事务内直接读取配置原始存储源 |
| 配置修改后需要全局生效 | 事务提交后更新Config配置 |
| 临时读取最新配置不影响全局 | 使用闭包隔离配置读取 |