本文目录导读:

在ThinkPHP框架中,数据库事务隔离级别的设置和管理是一个重要的话题,以下是详细的说明:
默认隔离级别
MySQL默认的隔离级别是可重复读(REPEATABLE READ),ThinkPHP默认使用MySQL,所以默认也是这个级别。
设置事务隔离级别的方法
直接执行SQL语句
use think\facade\Db;
// 查看当前隔离级别
$level = Db::query('SELECT @@tx_isolation');
// MySQL 8.0+ 使用
$level = Db::query('SELECT @@transaction_isolation');
// 设置隔离级别
Db::execute('SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED');
Db::execute('SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ');
Db::execute('SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED');
Db::execute('SET SESSION TRANSACTION ISOLATION LEVEL SERIALIZABLE');
在事务开始前设置
use think\facade\Db;
// 开启事务前设置隔离级别
Db::execute('SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED');
Db::startTrans();
try {
// 业务逻辑
Db::commit();
} catch (\Exception $e) {
Db::rollback();
throw $e;
}
配置全局默认
在config/database.php配置文件中:
return [
// 默认数据库连接配置
'connections' => [
'mysql' => [
'type' => 'mysql',
'hostname' => '127.0.0.1',
'database' => 'database_name',
'username' => 'root',
'password' => '',
'hostport' => '3306',
'charset' => 'utf8mb4',
'prefix' => '',
// 其他配置...
'params' => [
// 这里可以设置PDO参数
\PDO::ATTR_TIMEOUT => 5,
],
],
],
];
各隔离级别的优缺点
| 隔离级别 | 脏读 | 不可重复读 | 幻读 | 性能 |
|---|---|---|---|---|
| READ UNCOMMITTED | 可能 | 可能 | 可能 | 最高 |
| READ COMMITTED | 不会 | 可能 | 可能 | 高 |
| REPEATABLE READ | 不会 | 不会 | 可能(MySQL InnoDB解决) | 中 |
| SERIALIZABLE | 不会 | 不会 | 不会 | 最低 |
事务嵌套与隔离级别
use think\facade\Db;
try {
// 外层事务
Db::startTrans();
// 设置当前事务的隔离级别
Db::execute('SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED');
// 业务逻辑1
Db::table('users')->where('id', 1)->update(['balance' => 100]);
// 内层事务(ThinkPHP支持事务嵌套)
Db::startTrans();
try {
// 业务逻辑2
Db::table('orders')->insert(['user_id' => 1, 'amount' => 100]);
Db::commit();
} catch (\Exception $e) {
Db::rollback();
throw $e;
}
Db::commit();
} catch (\Exception $e) {
Db::rollback();
throw $e;
}
使用事务回调和监听器
use think\facade\Db;
// 使用事务回调
Db::transaction(function() {
// 可以在这里设置隔离级别
Db::execute('SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED');
// 业务逻辑
Db::table('users')->where('id', 1)->update(['balance' => 100]);
Db::table('orders')->insert(['user_id' => 1, 'amount' => 100]);
});
// 事务监听
Db::listen(function($sql, $time, $explain) {
// 监控SQL
});
// 开启调试模式查看事务
Db::transaction(function() {
$trace = debug_backtrace();
// 业务逻辑
});
注意事项
- 全局设置影响所有连接:使用
SET GLOBAL会影响所有会话,建议使用SET SESSION。
// 只影响当前会话
Db::execute('SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED');
// 会影响所有新连接(谨慎使用)
Db::execute('SET GLOBAL TRANSACTION ISOLATION LEVEL READ COMMITTED');
- 在事务开始前设置:隔离级别必须在事务开始前设置,否则对当前事务无效。
// 错误:事务开始后设置无效
Db::startTrans();
Db::execute('SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED'); // 无效
// 正确:事务开始前设置
Db::execute('SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED');
Db::startTrans();
- 连接池问题:如果使用连接池,需要注意理解隔离级别的设置是针对连接的。
// 使用连接池时,在获取连接后设置
$connection = Db::connect('mysql');
$connection->execute('SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED');
最佳实践
namespace app\common;
use think\facade\Db;
use think\exception\ValidateException;
class TransactionService
{
/**
* 带隔离级别的事务执行
*/
public static function executeWithIsolation(callable $callback, $isolationLevel = 'READ COMMITTED')
{
// 设置隔离级别
$validLevels = [
'READ UNCOMMITTED',
'READ COMMITTED',
'REPEATABLE READ',
'SERIALIZABLE'
];
if (!in_array($isolationLevel, $validLevels)) {
throw new \InvalidArgumentException('无效的事务隔离级别');
}
// 在事务开始前设置
Db::execute("SET SESSION TRANSACTION ISOLATION LEVEL {$isolationLevel}");
try {
Db::startTrans();
$result = $callback();
Db::commit();
return $result;
} catch (\Exception $e) {
Db::rollback();
throw $e;
}
}
}
// 使用示例
TransactionService::executeWithIsolation(function() {
Db::table('users')->where('id', 1)->update(['balance' => 100]);
Db::table('orders')->insert(['user_id' => 1, 'amount' => 100]);
}, 'READ COMMITTED');
选择适当的隔离级别需要根据业务需求权衡一致性和性能,如果不确定,建议保持MySQL默认的REPEATABLE READ级别,除非有特定的业务场景需要更改。