repeatable read下select总看不到新插入行,是因为事务启动时生成一致性视图并复用,mvcc确保快照读不感知其他事务已提交变更;可通过select @@tx_isolation和@@autocommit确认配置,降级为read committed或改用select ... for update可读最新数据。

Repeatable Read下SELECT为什么总看不到新插入的行
不是代码写错了,也不是数据库坏了,是MySQL在REPEATABLE-READ隔离级别下主动“冻结”了读视图。事务一执行第一条SELECT(或BEGIN后任意读操作),InnoDB就基于当前全局事务ID快照生成一个一致性视图(consistent read view),后续所有普通SELECT都复用它——哪怕其他会话已COMMIT新行,只要不在这个视图创建之后启动的事务中,就不会被看到。
这种行为由MVCC机制保障,不依赖锁,也不需要阻塞写入,是性能与一致性的权衡结果。
怎么确认当前连接正运行在REPEATABLE-READ
别猜,直接查。连上MySQL后执行这两条命令:
-
SELECT @@tx_isolation;—— 返回值通常是'REPEATABLE-READ' -
SELECT @@autocommit;—— PHP/Pymysql默认为0,即关闭自动提交,这会让每个显式事务更“顽固”地持有旧视图
如果两者都是默认值,那“查不到新数据”就是预期行为,不是bug。
想立刻读到其他事务刚提交的行,该怎么做
有两条路,选哪条取决于你是否还需要事务的原子性保障:
- 降级隔离级别:执行
SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED;,之后该连接所有新事务都会启用“每次SELECT都拉最新已提交版本”的语义 - 强制当前读:把普通
SELECT改成SELECT ... FOR UPDATE或SELECT ... LOCK IN SHARE MODE,它们绕过MVCC快照,直接读最新数据(但会加锁,影响并发) - 不推荐改全局配置:用
SET GLOBAL TRANSACTION ISOLATION LEVEL ...会影响所有新连接,容易引发意外行为
注意:READ COMMITTED 能解决“看不见新行”,但不解决幻读;而FOR UPDATE虽能读新行,但在高并发下可能引发死锁或锁等待。
PHP/Pymysql里最容易忽略的隐式事务陷阱
PHP用PDO或mysqli、Python用pymysql时,默认autocommit=False,且没显式BEGIN时,第一条SELECT也会开启隐式事务并固定视图。这意味着:
- 一次HTTP请求中多次调用
$pdo->query("SELECT ..."),第二次查的仍是第一次启动时的快照 - 连接池场景下,连接被复用,但上次事务的视图残留可能影响本次查询(尤其当连接未
commit/rollback就归还) - 简单设
autocommit=True可让每次查询都在独立短事务中执行,从而读到最新数据,但代价是失去跨语句的事务控制能力
真正难处理的,不是“怎么读到新数据”,而是“什么时候该读旧快照、什么时候必须读新数据”——业务语义决定隔离策略,不是技术能自动替你选的。











