select在未提交事务中看到的数据取决于隔离级别:repeatable read下读事务启动时的快照,read committed下读每次查询前已提交数据,read uncommitted下可读未提交脏数据。

SELECT 在未提交事务里看到什么数据?
取决于隔离级别,不是“会不会影响”,而是“按规则该看到什么”。REPEATABLE READ(MySQL 5.7+ 默认)下,事务启动瞬间就拍了个快照,之后不管其他事务有没有 COMMIT,这个事务里的所有 SELECT 都只读快照——哪怕外部刚插入、更新、删除了某行,它也看不见。
但注意:这个“看不见”仅限于本事务内。其他新开启的事务,如果隔离级别是 READ COMMITTED,就能立刻看到已提交的变更;如果是 READ UNCOMMITTED,甚至能看到别人还没 COMMIT 的脏数据。
-
REPEATABLE READ:快照基于事务第一次SELECT或UPDATE时间点,后续查询不感知外部提交 -
READ COMMITTED:每次SELECT都重新拍快照,能读到之前已提交的变更 -
READ UNCOMMITTED:不加行锁也不用快照,直接读最新页,可能读到回滚前的中间态(脏读)
为什么另一个会话的 UPDATE 会让我的 SELECT 卡住?
不是“查询被影响”,是“查询在等锁”。当会话 A 执行了 UPDATE products SET stock = stock - 1 WHERE id = 1 但没 COMMIT,InnoDB 会对 id = 1 这行加 X 锁(排他锁)。此时会话 B 执行 SELECT * FROM products WHERE id = 1,如果用了 SELECT ... FOR UPDATE 或 SELECT ... LOCK IN SHARE MODE,就会尝试加 S 锁 或 X 锁,和 A 的 X 锁 冲突,于是阻塞。
但普通 SELECT(无锁提示)在 REPEATABLE READ 下通常不会卡——它走的是 MVCC 快照,不争抢行锁。只有下面情况才会真被堵住:
- 你用了
SELECT ... FOR UPDATE或带锁读语句 - 你执行的是 DDL(比如
ALTER TABLE),需要表级元数据锁(MDL),而未提交事务正持有该表的 MDL 读锁 - 查询涉及唯一索引查找,且刚好命中被未提交事务修改的间隙(触发间隙锁等待)
information_schema.INNODB_TRX 能查到什么?
这是定位“谁在挂着事务不放”的第一站。运行:
SELECT trx_id, trx_state, trx_started,
TIMESTAMPDIFF(SECOND, trx_started, NOW()) AS run_sec,
trx_query, trx_mysql_thread_id
FROM information_schema.INNODB_TRX
WHERE TIMESTAMPDIFF(SECOND, trx_started, NOW()) > 60
ORDER BY run_sec DESC;
关键字段含义:
-
trx_state = 'RUNNING':事务还在活跃,没卡在锁上 -
trx_state = 'LOCK WAIT':它正在等别的事务释放锁 -
trx_query IS NULL:事务开了但没执行任何语句(常见于客户端连上就BEGIN然后发呆) -
run_sec超大(比如几小时):大概率是开发在客户端手动开事务后忘了COMMIT或ROLLBACK
查到线程 ID 后,用 KILL <em>thread_id</em> 强制结束(需有 SUPER 权限)。
应用代码里最容易漏掉 commit 的地方
不是语法写错,而是控制流绕过了 commit。典型场景:
- 异常分支没写
rollback,也没commit,连接关闭时事务自动回滚,但日志里看不出痕迹 - 用了连接池,
autoCommit = false后没显式commit,连接归还池子时事务状态被复位或丢弃(行为因驱动而异) - 事务里调用了远程服务或文件 IO,耗时长,期间连接超时断开,但数据库端事务仍开着
- Java 里用
@Transactional,但方法是private或调用发生在同一对象内,AOP 失效,事务根本没启
真正难排查的,从来不是“哪里该写 commit”,而是“哪条路径让它根本没走到 commit 那行”。











