MySQL分表自增ID解决方案

ps:如果这篇帖子对于还在找工作和找实习的你有所帮助,可以关注我,给本贴点赞、评论、收藏并订阅专栏;同时不要吝啬您的花花

分表后自增ID核心痛点:单表自增无法全局唯一,需同时满足唯一性、高性能、有序性、扩展性。以下是6类主流方案,附原理、配置、优缺点及选型建议,适配不同业务场景。

一、多库多表自增步长(数据库原生方案)

核心原理

利用MySQL auto_increment_increment(自增步长)和auto_increment_offset(起始偏移量)参数,为不同分库/分表分配不重叠ID区间,实现全局唯一。

  • 示例:2个分库,步长=2,库1起始=1,库2起始=2库1:ID=1,3,5,7...库2:ID=2,4,6,8...

配置示例(MySQL)

-- 单库多表:为每张表设置不同起始值+相同步长
ALTER TABLE order_0 AUTO_INCREMENT = 1;
ALTER TABLE order_0 AUTO_INCREMENT_INCREMENT = 2;

ALTER TABLE order_1 AUTO_INCREMENT = 2;
ALTER TABLE order_1 AUTO_INCREMENT_INCREMENT = 2;

-- 多库多表:修改全局参数(需重启生效,或会话级设置)
SET GLOBAL auto_increment_increment = 3; -- 步长=分库数量
SET GLOBAL auto_increment_offset = 1;   -- 库1起始=1
-- 库2会话设置
SET SESSION auto_increment_offset = 2;

优缺点

纯MySQL实现,无额外组件依赖

扩展性极差:步长固定后,新增分库需重新调整所有表的起始值

ID有序,索引性能优

单库单表存在单点瓶颈,高并发下易成为性能热点

实现简单,运维成本低

主从切换/故障时可能出现ID不连续

适用场景

  • 分库/分表数量固定且极少(如≤5个)
  • 并发量低,对扩展性无要求的小型系统

二、数据库号段模式(主流成熟方案)

核心原理

集中分配+本地缓存:单独建一张id_generator号段表,记录业务类型、当前号段起始值、步长、版本号;应用从DB批量获取一段ID(如1~1000),本地内存自增使用,用完再取下一段,减少DB请求次数。

表结构设计

CREATE TABLE id_generator (
    biz_type VARCHAR(50) NOT NULL COMMENT '业务类型(如order、user)',
    max_id BIGINT NOT NULL COMMENT '当前号段最大ID',
    step INT NOT NULL COMMENT '号段步长',
    version INT NOT NULL DEFAULT 0 COMMENT '乐观锁版本',
    update_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    PRIMARY KEY (biz_type)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT 'ID号段生成表';

核心流程

  1. 应用启动时,初始化号段(如插入biz_type=order,max_id=0,step=1000)
  2. 应用获取ID:从DB通过乐观锁更新max_id(UPDATE id_generator SET max_id = max_id + step WHERE biz_type = ? AND version = ?),获取新号段
  3. 本地内存维护当前号段的起始/结束值,自增分配,用完重复步骤2

优缺点

高性能:一次DB请求获取1000+ID,QPS可达百万级

依赖DB,单库号段表存在单点风险

ID有序,索引友好

号段耗尽瞬间会有短暂DB请求延迟

扩展性较好:调整step即可扩容,新增业务只需新增记录

需手动实现乐观锁,开发成本略高

适用场景

  • 中高并发业务(如订单、支付)
  • 希望纯数据库方案,不想引入额外中间件

三、雪花算法(Snowflake)及优化版(首选方案)

核心原理

Twitter开源的分布式ID算法,生成64位long型整数,本地生成无外部依赖,高性能且有序。

结构(标准64位):

固定0

毫秒级时间戳(可用69年)

机器/节点ID(支持1024个节点)

同一毫秒内序列号(支持4096个/毫秒)

优化版:Leaf-Snowflake(美团)

解决原生Snowflake时钟回拨机器ID分配问题,通过ZooKeeper动态分配机器ID,优化时钟回拨处理。

Java实现示例(标准Snowflake)

public class SnowflakeIdGenerator {
    // 起始时间戳:2020-01-01 00:00:00
    private static final long EPOCH = 1577836800000L;
    private static final long WORKER_ID_BITS = 10L;
    private static final long SEQUENCE_BITS = 12L;

    private static final long MAX_WORKER_ID = (1L << WORKER_ID_BITS) - 1;
    private static final long MAX_SEQUENCE = (1L << SEQUENCE_BITS) - 1;

    private static final long WORKER_ID_SHIFT = SEQUENCE_BITS;
    private static final long TIMESTAMP_SHIFT = SEQUENCE_BITS + WORKER_ID_BITS;

    private final long workerId;
    private long lastTimestamp = -1L;
    private long sequence = 0L;

    public SnowflakeIdGenerator(long workerId) {
        if (workerId < 0 || workerId > MAX_WORKER_ID) {
            throw new IllegalArgumentException("机器ID超出范围(0-1023)");
        }
        this.workerId = workerId;
    }

    public synchronized long nextId() {
        long currentTimestamp = System.currentTimeMillis();
        // 处理时钟回拨
        if (currentTimestamp < lastTimestamp) {
            throw new RuntimeException("时钟回拨,无法生成ID");
        }
        if (currentTimestamp == lastTimestamp) {
            sequence = (sequence + 1) & MAX_SEQUENCE;
            if (sequence == 0) {
                // 同一毫秒序列号耗尽,等待下一毫秒
                currentTimestamp = waitNextMillis(lastTimestamp);
            }
        } else {
            sequence = 0L;
        }
        lastTimestamp = currentTimestamp;
        // 拼接ID
        return ((currentTimestamp - EPOCH) << TIMESTAMP_SHIFT)
                | (workerId << WORKER_ID_SHIFT)
                | sequence;
    }

    private long waitNextMillis(long lastTimestamp) {
        long timestamp = System.currentTimeMillis();
        while (timestamp <= lastTimestamp) {
            timestamp = System.currentTimeMillis();
        }
        return timestamp;
    }
}

优缺点

高性能:本地生成,QPS可达百万级,无网络开销

依赖系统时间,时钟回拨会导致ID重复

ID有序,趋势递增,索引性能优

机器ID需手动/工具分配(优化版可通过ZK解决)

无外部依赖,扩展性强

64位ID可能超出部分语言/框架的支持范围(如部分ORM)

适用场景

  • 高并发分布式系统(电商、金融、社交)
  • 对ID有序性要求高,希望无外部中间件依赖

四、Redis自增/自增队列方案

核心原理

利用Redis的INCR/INCRBY命令生成自增ID,单节点性能极高,支持分布式部署。

  • 简单模式:单Redis节点,SET order_id:1 0,每次INCR order_id:1获取ID
  • 集群模式:按业务类型分Key,或用Redis Cluster保证高可用

优化:Redis自增队列

预生成一批ID到Redis List,应用从List批量获取,减少命令调用次数,提升性能。

优缺点

极致性能:单节点QPS可达10万+,集群可更高

依赖Redis,需维护Redis集群

支持分布式,扩展性强

ID有序,但跨节点可能不连续(集群模式)

实现简单,支持多业务类型隔离

Redis故障会导致ID生成失败(需做持久化/高可用)

适用场景

  • 已部署Redis集群的系统
  • 对性能要求极高,且能接受Redis依赖的业务

五、UUID/UUID雪花版(备选方案)

核心原理

生成128位全局唯一ID,标准UUID(如3f2504e0-4f89-11d8-97d0-000a95df5273),或优化版UUID(如雪花+UUID结合)。

优缺点

全局唯一,无冲突风险

ID无序,索引性能极差(插入/查询慢,碎片多)

实现简单,无需额外配置

128位ID占用存储空间大(是long型的2倍)

适合跨系统ID传递

可读性差,业务含义不明确

适用场景

  • 跨系统ID传递(如第三方接口)
  • 对有序性无要求,且能接受索引性能损耗的场景

六、分库分表中间件封装方案(Sharding-JDBC/MyCat)

核心原理

通过分库分表中间件封装ID生成逻辑,应用无需关心ID生成细节,直接使用中间件提供的主键生成策略。

示例:Sharding-JDBC(SpringBoot)

spring:
  shardingsphere:
    datasource:
      names: ds0,ds1
      ds0:
        type: com.zaxxer.hikari.HikariDataSource
        jdbc-url: jdbc:mysql://localhost:3306/db0
      ds1:
        type: com.zaxxer.hikari.HikariDataSource
        jdbc-url: jdbc:mysql://localhost:3306/db1
    sharding:
      tables:
        t_order:
          actual-data-nodes: ds$->{0..1}.t_order_$->{0..1}
          # 主键生成策略:雪花算法
          key-generator:
            column: order_id
            type: SNOWFLAKE
            props:
              worker.id: 1 # 机器ID
          # 分表策略
          table-strategy:
            inline:
              sharding-column: user_id
              algorithm-expression: t_order_$->{user_id % 2}

优缺点

开箱即用,无需手动实现ID生成逻辑

引入中间件,增加架构复杂度

支持多种ID生成策略(雪花、号段、自增)

学习成本较高,需熟悉中间件配置

与分库分表逻辑无缝集成,运维统一

版本升级/迁移成本较高

适用场景

  • 已使用Sharding-JDBC/MyCat等分库分表中间件的系统
  • 希望一站式解决分表+ID生成问题的团队

七、方案对比与选型建议

多库步长

MySQL

固定分表,低并发

数据库号段

MySQL

中高并发,纯数据库方案

雪花算法

极高

系统时间

高并发,无中间件依赖

Redis自增

极高

Redis

已用Redis,极致性能

UUID

极高

跨系统传递,低性能要求

Sharding-JDBC

分库分表中间件

已用分库分表中间件

选型优先级(2026实战)

  1. 首选:雪花算法(标准/Leaf优化版)—— 兼顾性能、有序性、扩展性,无额外依赖
  2. 次选:数据库号段模式 —— 纯数据库方案,适合不想引入Redis/中间件的团队
  3. 补充:Sharding-JDBC封装方案 —— 已用分库分表中间件的场景
  4. 备选:Redis自增 —— 已有Redis集群的高并发场景
  5. 不推荐:多库步长/UUID —— 扩展性/性能存在明显短板

八、生产环境关键注意事项

  1. 雪花算法时钟回拨:部署NTP时间同步服务,或使用Leaf-Snowflake等优化版解决
  2. 号段模式高可用:号段表做多库冗余,通过主从复制/双活保证可用性
  3. ID长度设计:优先使用64位long型(如雪花、号段),避免使用128位UUID,提升索引性能
  4. 业务ID隔离:不同业务类型使用独立的ID生成器/Key,避免ID冲突
  5. 监控告警:监控ID生成成功率、延迟、机器ID分配情况,及时发现故障

需要我根据你的具体场景(分表数量、并发量、现有组件)给出定制化方案和核心配置清单吗?

ps:如果这篇帖子对于还在找工作和找实习的你有所帮助,可以关注我,给本贴点赞、评论、收藏并订阅专栏;同时不要吝啬您的花花

全部评论

相关推荐

03-10 17:28
南京大学 Java
1.&nbsp;代码考核题1:找出长度最小的子数组2.&nbsp;代码考核题2:SQL题目,涉及两张表连接、按日期分组计算人均PV3.&nbsp;请做一个自我介绍4.&nbsp;你的项目是学校课程作业还是自己学习的?具体介绍一下项目来源5.&nbsp;你的商城项目考虑了高并发,说一下整体架构设计思路6.&nbsp;说一下你的部署方案7.&nbsp;解释一下JWT&nbsp;+&nbsp;Redis双token机制的工作原理,以及相比传统session登录的优势8.&nbsp;Redis有几种数据结构,各种数据结构的特点和优缺点是什么?9.&nbsp;为什么使用Redis&nbsp;+&nbsp;Lua脚本来扣减库存?10.&nbsp;在Lua脚本里面怎么定义一个变量?11.&nbsp;订单和库存的数据一致性是怎么保证的?是什么级别的一致性(最终一致性还是实时一致性)?12.&nbsp;多级缓存(Caffeine&nbsp;+&nbsp;Redis)架构存在哪些问题?分布式部署时又会遇到什么问题?13.&nbsp;订单智能释放使用了RocketMQ延迟消息+定时任务兜底方案,为什么不能只用RocketMQ延迟消息?14.&nbsp;如果项目运行中接口突然变慢,怎么去查找问题所在并解决?15.&nbsp;你的Agent项目是怎么做的?有没有知识库训练?16.&nbsp;意图判断和意图识别是怎么处理的?17.&nbsp;prompt是谁写的?是内置的还是用户自己写?18.&nbsp;你们有统一的家居行业知识库吗?是给大模型提前训练还是有现成的针对家居的抓手大模型?19.&nbsp;你是怎么使用AI&nbsp;coding的?第一次面试,准备得很不充分,刚上来就是代码题有点紧张。面试官是s3的,难道技术提前批都是去s3?感觉基本寄了,上来直接问部署细节(我本来想答K8s,docker之类的但不熟就没说)+&nbsp;语法细节,很明显就知道项目不是我从头到尾自己手搓的(我说用了AI&nbsp;coding)没有问八股(很奇怪),一直在拷打项目,答得整体一般
点赞 评论 收藏
分享
评论
点赞
收藏
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务