GORM中MySQL可通过Session(&gorm.Session{IsolationLevel:...})直接设置隔离级别,而PostgreSQL必须用tx.Exec("SET TRANSACTION ISOLATION LEVEL...")显式配置,且仅对MySQL和SQL Server生效。

MySQL:用 Session() 设置隔离级别最直接
GORM 对 MySQL 原生支持隔离级别配置,调用 Session(&gorm.Session{IsolationLevel: ...}) 即可生效。注意它只在 MySQL(和 SQL Server)上起作用,不是跨库通用接口。
常见写法:
tx := db.Session(&gorm.Session{IsolationLevel: sql.LevelReadCommitted}).Begin()
可用值包括:sql.LevelReadUncommitted、sql.LevelReadCommitted、sql.LevelRepeatableRead、sql.LevelSerializable。这些是 Go 标准库 database/sql 定义的常量,GORM 直接复用。
- 必须在
Begin()之前设置,否则无效 - 若事务已开启再调用
Session(),不会修改当前事务的隔离级别 - MySQL 默认是
REPEATABLE READ,但高并发更新场景下容易触发间隙锁争用,建议按需降级为READ COMMITTED
PostgreSQL:必须用 Exec() 执行原生命令
GORM 的 IsolationLevel 字段对 PostgreSQL 完全无效——它不翻译成 SET TRANSACTION ISOLATION LEVEL,也不会在 BEGIN 后自动插入该语句。你得手动执行。
正确顺序不可颠倒:
tx := db.Begin()
tx.Exec("SET TRANSACTION ISOLATION LEVEL REPEATABLE READ")
// ↑ 必须紧接 BEGIN 后、任何 DML 前
- PostgreSQL 的
REPEATABLE READ实际语义等价于 SQL 标准的SERIALIZABLE,能防幻读,但代价是可能触发序列化失败(SQLSTATE 40001),需重试逻辑 - 不能写成
SET SESSION CHARACTERISTICS AS TRANSACTION ISOLATION LEVEL ...—— GORM 事务内不识别该语法 - 如果用
db.Transaction()闭包形式,记得在闭包开头就tx.Exec(),别等到中间才设
事务开启前就设全局隔离级别?别这么做
有人试图通过 db.Session(&gorm.Session{...}).First() 给普通查询设隔离级别,或在 gorm.Open() 后执行 db.Exec("SET SESSION ..."),这都没用。
原因很实在:
- 普通查询(非事务)不走隔离级别控制,数据库按会话默认级别执行
-
SET SESSION只影响当前连接,而 GORM 连接池里的连接是复用的,你设了别人也可能覆盖 - 想“一劳永逸”地锁死所有事务级别?只能靠规范+代码审查,GORM 没提供这种机制
隔离级别选错会导致什么
不是所有业务都需要 REPEATABLE READ。选高了,性能掉得明显;选低了,数据逻辑出问题。
典型表现:
- MySQL 下
REPEATABLE READ+ 高频UPDATE ... WHERE x IN (...)→ 大量Lock wait timeout exceeded - PostgreSQL 下漏设
REPEATABLE READ,却依赖“两次 SELECT 结果一致” → 出现不可重复读,前端看到数据闪动 - 跨服务调用中,A 服务用
READ COMMITTED,B 服务用REPEATABLE READ,联合查数时语义不一致,审计对不上
真正要盯住的,从来不是“怎么设”,而是“为什么设这个值”——得从具体读写模式、失败容忍度、上下游契约出发判断,而不是抄个示例就提交。


















