单库订单表直接用auto_increment可行的条件是:未分库分表、qps不超5000、不依赖id做业务排序或时间推断;此时它写入快、索引友好、查询稳定,order by id desc可天然按插入时间倒序。

单库订单表直接用 AUTO_INCREMENT 什么时候可行?
只要你的订单系统还没分库分表、QPS 不超过 5000、且不依赖 ID 做业务排序或时间推断,AUTO_INCREMENT 就是最省心的选择。它写入快、索引友好、查询稳定,连 ORDER BY id DESC 都能天然按插入时间倒序。
但要注意两个硬限制:
- 不能跨实例复用:一旦未来要拆成
orders_01到orders_16,各分片的id=1001会撞车,下游聚合查不出完整订单流 - 不能暴露增长速率:攻击者通过连续下单观察 ID 间隔,就能反推日活或库存余量
分库后仍想用 MySQL 自增,必须配对设置 auto_increment_offset 和 auto_increment_increment
这是 Flickr 2010 年提出的“Ticket Server”方案,本质是让每个数据库实例只负责一段 ID 空间。假设有 4 台订单库节点,就得统一执行:
SET GLOBAL auto_increment_offset = 1; SET GLOBAL auto_increment_increment = 4;
然后依次在每台机器上设不同 offset:
- Node1:
auto_increment_offset = 1→ 生成1, 5, 9, 13... - Node2:
auto_increment_offset = 2→ 生成2, 6, 10, 14... - Node3:
auto_increment_offset = 3→ 生成3, 7, 11, 15... - Node4:
auto_increment_offset = 4→ 生成4, 8, 12, 16...
这个方案不依赖额外服务,但有个致命盲区:所有节点必须严格同步配置,且不能动态增减节点——加一台新库,步长和 offset 全得重算,已有数据还得迁移 ID 范围,线上不敢轻易动。
用专用 id_generator 表做“中心化自增”,比改全局变量更可控
建一张独立表,靠唯一业务类型(如 'order')锁住一行记录,每次更新触发自增:
CREATE TABLE `id_generator` ( `id` BIGINT NOT NULL AUTO_INCREMENT, `biz_type` VARCHAR(32) NOT NULL, `update_time` DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (`id`), UNIQUE KEY `uk_biz_type` (`biz_type`) ) ENGINE=InnoDB;
获取 ID 的核心逻辑是原子更新 + 查询:
- 先
UPDATE id_generator SET update_time = NOW() WHERE biz_type = 'order' - 再
SELECT id FROM id_generator WHERE biz_type = 'order'
这种方式规避了全局变量变更风险,也支持多业务隔离('user' 和 'order' 各走各的 ID 序列),但要注意:这张表会成为单点瓶颈,QPS 超过 3000 就得考虑号段模式预取。
真正高并发场景下,别硬扛 MySQL 自增,优先考虑号段模式或 Snowflake
当订单峰值突破 1w QPS,上面所有基于 MySQL 自增的方案都会卡在行锁或主从延迟上。这时候应该切换思路:
- 号段模式:应用层一次取 1000 个 ID(如
1000001~1001000),用完再请求下一段,MySQL 只需承担 1/1000 的压力 - Snowflake:用时间戳+机器ID+序列号拼出 64 位整数,完全去中心化,但要注意时钟回拨问题
- Redis
INCR:适合中小规模,但 Redis 故障时 ID 生成中断,得有降级兜底(比如切到本地缓存+时间戳)
最容易被忽略的一点:无论选哪种方案,ID 必须在写入订单主表前就确定,不能在事务里临时生成——否则事务回滚会导致 ID “空洞”,长期运行后 ID 碎片化严重,影响范围查询性能。











