标准insert在分布式sql中不等于原子写入,因其实际执行是解析→分片路由→并行下发→各节点独立提交,若某分片宕机或网络中断,易导致部分成功、状态不确定的“半写”问题。

跨节点 INSERT 操作无法靠单条 SQL 语句自动保证强一致性——必须依赖底层事务协议或应用层协同机制,否则极易出现部分节点写入成功、部分失败的“半写”状态。
为什么标准 INSERT 在分布式 SQL 中不等于原子写入
多数分布式 SQL 数据库(如 CockroachDB、TiDB、YugabyteDB)将单条 INSERT 视为“客户端发起的请求”,而非天然跨节点原子操作。其实际执行路径是:解析 → 分片路由 → 并行下发到多个分片节点 → 各节点独立提交。若某分片节点宕机或网络中断,其他节点可能已落盘,而协调器未收到全部 ACK,此时事务状态即进入不确定(in-doubt)。
- MySQL Group Replication 的
INSERT默认只保证“组内多数派写入成功”,但不阻塞客户端直到所有节点同步完成 - PostgreSQL Citus 扩展中,
INSERT INTO distributed_table会按分布键哈希路由,但各目标节点 commit 时间点不一致,无全局锁协调 - AlaSQL 等嵌入式 SQL 引擎根本不提供跨实例事务语义,所谓“分布式 INSERT”纯属应用层拼接
必须启用的底层协议:2PC 或 Raft-based 事务提交
真正能约束跨节点 INSERT 原子性的,只有两类机制:两阶段提交(2PC)或基于共识日志的单次提交(如 Raft log append + apply)。前者见于 Oracle XA、PostgreSQL 的 PREPARE TRANSACTION;后者是现代 NewSQL 的主流,如 TiDB 的 Percolator 协议、CockroachDB 的 Raft group 提交流程。
- 启用前确认数据库是否默认开启:CockroachDB 默认强制使用
STRICT SERIALIZABLE隔离级 + Raft 日志同步,INSERT天然具备跨节点原子性;TiDB 则需确保tikv_gc_life_time和tidb_txn_mode='optimistic'配置匹配业务场景 - 避免手动绕过:不要用
INSERT ... ON DUPLICATE KEY UPDATE替代事务,它仅在单分片内生效,跨分片时 key 冲突检测失效 - 注意隔离级别副作用:在
READ COMMITTED下,多个并发INSERT可能因读取不同快照导致唯一约束冲突被延迟报出
应用层兜底:幂等 + 补偿 + 状态跟踪
即便数据库声称支持分布式事务,网络分区或 coordinator 崩溃仍会导致事务卡在 prepare 状态。此时必须由应用承担最终一致性责任。
- 为每条跨节点
INSERT生成唯一request_id,写入本地事务状态表(含 status: pending/committed/rolled_back) - 插入前先查该
request_id是否已存在,避免重复写入;失败后触发异步补偿任务,调用SELECT FOR UPDATE锁定关联记录再修正 - 慎用“先写缓存再批量刷库”:Redis 中暂存待
INSERT数据,看似提升吞吐,实则把数据库一致性问题转移到缓存与 DB 的双写时序上
最容易被忽略的是分片键设计本身——如果 INSERT 的数据天然无法路由到同一分片(例如用用户 ID 分片,但插入订单时只传了商品 ID),那任何事务协议都救不了你。一致性不是加个 BEGIN 就能来的,得从数据如何切分开始想清楚。











