结论:那两条前置确实是最容易白跑的地方,尤其 binlog_format,很多人本地 Docker 里测(容器默认没开 binlog),一到 CI 或预发才炸。
动手前先一条语句把环境盘清楚:SELECT @@version, @@transaction_isolation, @@binlog_format。5.7 得用 tx_isolation,8.0 之后 tx_ 别名已经没了;8.0 默认就是 ROW,较新的 8.4 起 binlog_format 基本退场(只剩 ROW),所以这条坑主要集中在 5.7 和早年改过配置的老实例上——先用版本号定位,别一上来就怀疑代码。
rowCount 你补的这个反方向坑我认同,再加一句实操:PDO::MYSQL_ATTR_FOUND_ROWS 是连接级选项,只能在 new PDO() 时给,连上之后改不了,所以「局部处理」实际含义是单开一条连接,代价比想象大。更省事的做法是把判断挪进 SQL:MySQL 用 WHERE id=? AND version=? 的影响行数 0/1 直接当结果,PG 用 UPDATE ... RETURNING version 取回值比对,两边都不依赖驱动语义,换 ORM 也不受影响。ORM 内置乐观锁确实多是自读旧值比对、不吃这个坑,但手写 SQL 的地方恰恰最容易漏。
分页那条同意补 tiebreaker,但提醒一句:加了 id 之后复合索引要跟着调(比如 status, created_at, id),否则本来走索引排序的查询会退成 filesort,深翻页反而更慢。
结尾给个延伸:双轨跑可以再进一步——起个 PG 影子库(Docker 一条命令),同一份回归集两边各跑一遍做 diff,比在 MySQL 里模拟 PG 语义更接近真相,成本也就半天。

正式会员
认证极客










