mysql单机高并发下,合理配置事务隔离级别(read committed/repeatable read)+唯一索引/主键约束,即可支撑每秒数千写入并保证强一致性;分布式事务如xa、seata仅在跨库且无法合并单库时才需考虑。

MySQL单机高并发下,靠事务隔离级别和索引就能扛住一致性压力
绝大多数业务场景根本用不上分布式事务——MySQL在合理配置下,单实例配合 READ COMMITTED 或 REPEATABLE READ 隔离级别 + 正确的唯一索引/主键约束,足以应对每秒数千次的写入并保证强一致性。
常见错误现象:UPDATE account SET balance = balance - 100 WHERE user_id = 123 AND balance >= 100 被并发执行多次,导致余额透支;或者两个事务同时读到相同余额,各自扣减后写回,出现“超卖”。
- 必须给关键字段加
UNIQUE约束或PRIMARY KEY,否则SELECT ... FOR UPDATE可能锁不住预期行(比如范围查询没走索引) - 更新语句务必把校验逻辑(如余额判断)写进
WHERE条件,而不是先SELECT再UPDATE - 避免长事务:持有
FOR UPDATE锁超过 1 秒,就容易引发锁等待堆积,innodb_lock_wait_timeout默认 50 秒,但实际应控制在 100ms 内
什么时候必须上 XA 或 Seata?只有一种情况:跨库更新且无法合并为单库
MySQL 自带的 XA START/XA COMMIT 是两阶段提交协议实现,但它在高并发下性能极差、运维复杂、崩溃恢复难,生产环境基本没人用。
真实使用场景极少:比如订单库和库存库物理分离,且业务不允许通过消息队列+本地事务表做最终一致;又或者监管要求必须强一致、不允许任何中间态。
- 只要能把数据放在同一个 MySQL 实例(哪怕不同 schema),就别拆库——
XA的吞吐通常不到普通事务的 1/10 -
Seata AT模式本质是基于 undo log 的补偿事务,它不解决并发冲突,只是把“失败回滚”变得自动化;如果两个服务同时修改同一行,照样会报GlobalTransaction is not active或死锁 - 跨库操作优先考虑异步化:用
INSERT INTO t_local_tx (biz_id, status) VALUES (...)记录本地事务状态,再发 MQ 触发下游,靠定时任务对账
并发更新同一行时,InnoDB 行锁到底锁什么?
不是锁“值”,也不是锁“SQL”,而是锁索引记录(record lock)、间隙(gap lock)或两者组合(next-key lock)。理解这点,才能避开幻读和死锁陷阱。
典型错误:SELECT * FROM order WHERE status = 'pending' ORDER BY created_at LIMIT 1 FOR UPDATE —— 如果 status 没索引,InnoDB 会升级为表锁;即使有索引,也可能因为 ORDER BY + LIMIT 锁住多个间隙,导致无关行被阻塞。
- 确保
FOR UPDATE查询走的是唯一索引或主键,例如SELECT ... FROM user WHERE id = 123 FOR UPDATE,这样只锁单行,开销最小 - 如果必须按非唯一条件查询,请显式加
SELECT ... FROM t WHERE a = ? LOCK IN SHARE MODE并配合应用层重试,比FOR UPDATE更轻量 -
INSERT ... ON DUPLICATE KEY UPDATE是原子操作,天然防并发插入重复,比先查后插+事务更可靠
binlog 格式选 ROW 还是 STATEMENT?高并发下没得选
必须用 ROW。STATEMENT 在并发更新下极易导致主从不一致,尤其是涉及函数(NOW()、UUID())、子查询、或触发器时。
影响不止于复制:MySQL 8.0+ 的并行复制(slave_parallel_type = LOGICAL_CLOCK)依赖 ROW 格式解析变更行,否则从库只能单线程回放,延迟飙升。
-
binlog_row_image = FULL(默认)确保所有列变更都记录,便于闪回和审计;若只关心主键变更,可设为MINIMAL,但会增加解析难度 - 开启
binlog_transaction_compression = ON(MySQL 8.0.20+)可降低高并发写入时的 IO 压力,压缩率通常达 3–5 倍 - 注意
max_binlog_size不要设太小(如 100M),否则频繁切日志会加剧刷盘抖动,在写入峰值期可能触发Binlog cache memory exhausted
事情说清了就结束。真正卡住一致性的,从来不是理论模型,而是索引有没有建对、WHERE 条件有没有下推、事务有没有意外变长——这些细节藏在慢查询日志和 INFORMATION_SCHEMA.INNODB_TRX 里,不在架构图上。










