MySQL 自增主键热点与分布式 ID 实践
在高并发写入场景下,InnoDB 的自增主键(AUTO_INCREMENT)会在聚簇索引上形成尾部写热点,导致插入抖动与间歇性锁等待。本文从 InnoDB 聚簇索引结构、插入缓冲、间隙锁协同等维度拆解热点成因,并给出分布式 ID 的落地实践与权衡。
1. 聚簇索引与尾部写热点
- InnoDB 的聚簇索引以主键排序存储,AUTO_INCREMENT 会将新记录追加到 B+Tree 右侧叶子。
- 高并发插入时,右侧叶子页存在锁竞争与页分裂放大,表现为插入 TPS 下降与 P95/P99 波动。
CREATE TABLE orders (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
user_id BIGINT NOT NULL,
amount DECIMAL(10,2) NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
KEY idx_user (user_id)
) ENGINE=InnoDB;
2. AUTO_INCREMENT 锁与并发
- MySQL 5.7 之前,AUTO_INCREMENT 使用表级锁(不同模式);8.0 之后改进为持久化计数器与更细粒度控制,但热点依旧存在。
- 插入批量提交(group commit)可缓解 WAL 压力,但右边界叶子页仍是竞争点。
3. 方案对比
- 雪花算法(Snowflake):时间戳 + 机房ID + 机器ID + 自增序列,单机内有序,跨机整体趋势递增。
- 数据库号段(Segment):业务方一次申请号段,内存发号,落库更新游标,吞吐高但需要容灾与幂等。
- Redis INCR:实现简单,单点需高可用(主从/哨兵/集群),序列漂移与冷启动需评估。
| 方案 | 顺序性 | 吞吐 | 复杂度 | 依赖 |
|---|---|---|---|---|
| AUTO_INCREMENT | 强 | 中 | 低 | MySQL |
| Snowflake | 趋势递增 | 高 | 中 | 时钟、节点元数据 |
| 号段 | 趋势递增 | 极高 | 中 | DB 持久游标 |
| Redis INCR | 强 | 高 | 低 | Redis 可用性 |
4. 索引与二级索引代价
- 主键变长(如雪花 ID)会放大二级索引的指针大小(leaf 指向 PK),读放大明显。
- 建议:保持主键定长 BIGINT,避免 UUID(36) 直接做 PK,可用 BINARY(16) 存储 UUIDv1/v7。
5. 可复现实验(sysbench + 尾部热点)
# 1) 准备数据与表结构
mysql -e "DROP DATABASE IF EXISTS demo; CREATE DATABASE demo;"
mysql demo < <(cat <<SQL
CREATE TABLE t_hot (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
k BIGINT NOT NULL,
v VARCHAR(64) NOT NULL,
KEY idx_k(k)
) ENGINE=InnoDB;
SQL
)
# 2) 并发插入压测(64 线程,持续 120s)
sysbench oltp_insert --mysql-db=demo --table-size=0 \
--threads=64 --time=120 run
# 3) 观察:
# - performance_schema.events_waits_summary_global_by_event_name 中的锁等待
# - information_schema.INNODB_METRICS 中的页分裂、btr_node_split
对照改为雪花 ID(应用侧生成)并将 id 定长 BIGINT,重复压测,观察 TPS 与 P95 改善幅度。
6. 源码走读要点(InnoDB)
- btr0cur.cc:B+Tree 游标在右侧页插入与分裂路径;
- trx0i_s.cc:自增计数器的持久化与并发控制;
- log0write.cc:group commit 对日志刷盘合并的影响。
关注点:右侧叶子页 latch 竞争、页分裂代价、secondary index 指针放大。
7. 生产变更与回滚手册
1) 灰度:在影子表启用新 ID 策略,双写比对一致性(ID 单调、越界、重复)。 2) 切换:将业务写流量按 5%/20%/50%/100% 提升;监控写延迟、死锁、页分裂。 3) 回滚:保留开关,出现异常立即退回 AUTO_INCREMENT;影子表数据对账。
8. 观测指标基线
- 写延迟 P95/P99、QPS、redo fsync 次数;
- btr_page_split、lock_time、待提交事务数;
- 二级索引大小增幅(变长 PK 带来的指针放大)。
9. FAQ
- 雪花 ID 时钟回拨怎么办?
- NTP + 单调时钟守护;回拨检测时暂停发号并降级到号段。
- 二级索引变大影响查询?
- 评估热点查询是否可走覆盖索引/缩短选择列;必要时引入只读副本。