数据库分表之后id主键的处理办法详解

 更新时间:2026年08月17日 08:51:59   作者:杨夏同学  
这篇文章主要介绍了数据库分表之后id主键的处理办法,本文深入对比UUID、号段模式、雪花算法等主流方案,剖析各自优劣与适用场景,掌握全局唯一、趋势递增的ID生成核心技巧,需要的朋友可以参考下

前言

MySQL 分库分表(水平拆分)后,全局唯一且趋势递增的主键ID的设计是一个核心挑战。不能再使用单个数据库表的自增ID(AUTO INCREMENT),因为它只能在单个数据库的单个表内保证唯一和递增。分库分表环境下需要全局唯一 ID。

数据库分表后,主键ID的处理是分布式系统的核心挑战之一。

分表后ID核心要求

特性说明重要性
全局唯一整个分布式系统绝对唯一,不能出现重复⭐⭐⭐⭐⭐
趋势递增利于索引性能及排序⭐⭐⭐⭐
高可用ID生成服务必须容错⭐⭐⭐⭐⭐
低延迟/高性能生成速度不影响业务吞吐⭐⭐⭐⭐
易扩展支持分表数量动态扩容,能够应对业务增长压力⭐⭐⭐

主流ID生成方案详解

UUID

-- 生成示例:550e8400-e29b-41d4-a716-446655440000
SELECT UUID();
  • 原理:基于时间、网卡 MAC 地址、随机数等生成一个(通常表示为32位16进制数)的全局唯一标识符。
  • 优点:实现简单(几乎所有语言都有内置库)本地生成,无网络开销,性能极高。全球唯一性保证非常好。
  • 缺点
    • 完全随机无序:无序性导致索引碎片(B+树分裂),插入数据库时会导致严重的页分裂和索引碎片,极大影响写入性能(特别是 InnoDB 的聚集索引特性)。这是其最大缺点,通常不推荐用作数据库主键。
    • 存储空间大:长度过长(128 位/32 字符),作为主键存储空间大,索引效率低。
    • 可读性差
  • 适用:小型临时系统,对存储和性能要求不高,且需要强唯一性的非核心业务;或用作关联其他系统的唯一标识(如 Session ID,文件 ID)

数据库自增ID

集中分配:

  • 原理:单独建立一个数据库(或一个数据库中的一张表),专门用于生成 ID。该表使用 AUTO INCREMENT 功能。
  • 优点:简单容易实现, 每次业务系统需要ID 时,向该表插入一条空记录,获取返回的自增 ID。
  • 缺点:性能差,不推荐。

号段模式(Segment):常用且推荐的方式

  • 原理:业务系统启动时或 ID 用完时,向中心数据库批量申请一个号段(例如:1000~2000)。中心数据库更新最大值(当前最大值 + 步长,如 1000+1000=2000),并返回号段范围(1000~2000)。业务系统在本地内存中使用这个号段(1000,1001,1002… 2000)分配完后再去申请新号段。
  • 优点
    • 生成的 ID 是数字,长度可控。
    • 趋势递增(号段内连续,号段间递增)
    • 性能较高(批量获取,减少数据库交互)
    • 易于扩展(可部署多个中心数据库实例,通过设置不同初始值和相同步长来避免冲突,如:DB1初始1步长 1000, DB2 初始 2 步长 1000)。
  • 缺点
    • 重启服务器可能会导致内存号段丢失
    • 中心数据库是潜在的单点故障(需要高可用部署:主从、集群)
    • 仍然依赖数据库,数据库性能可能成为瓶颈(虽然号段模式大大缓解)
    • 号段用完时,请求新号段会有轻微延迟

Redis原子自增

# Python示例
import redis
r = redis.Redis()
def next_id(shard_id):
    key = f"user_id:{shard_id}"
    return r.incr(key)  # 原子操作
  • **原理:**利用Redis的INCR/INCRBY命令原子性来生成自增 ID。
  • 实现方式:
    • 简单 INCR:INCR global id key。性能优于数据库简单获取,但仍需每次请求
    • 号段模式:类似数据库号段模式。使用INCRBY global_id_key step 一次性获取一个号段范围,然后在本地内存消费。
  • 优点
    • 性能极高(Redis 内存操作)
    • 生成的 ID 是数字,长度可控
    • 趋势递增
  • 缺点
    • 数据持久化问题:Redis宕机可能导致号段丢失(即使有 AOF/RDB恢复点可能不是最新状态)。需要仔细配置持久化策略,存在丢失一小部分 ID 的风险。这是最大缺点
    • 运维复杂度高
    • 需要解决 Redis 单点/集群问题(高可用部署)
  • 适用:缓存层完备的高并发系统

Snowflake算法(主流方案)

// Java实现核心逻辑
long generateId() {
    long timestamp = System.currentTimeMillis() - START_TIME;
    return (timestamp << TIMESTAMP_SHIFT) |
           (dataCenterId << DATACENTER_SHIFT) |
           (machineId << MACHINE_SHIFT) |
           (sequence++ & MAX_SEQUENCE);
}
  • **原理:**通过算法生成一个64位的Long型ID。

  • 优点:

    • 高性能(单机每秒26万+)
    • 趋势递增
    • 无中心化依赖
  • 缺点:

    • 时钟回拨问题: 这是最大挑战。如果服务器系统时间发生回退(如NTP 同步),可能导致生成重复ID
    • 依赖机器配置:需要为每台部署 IDI生成服务的机器配置唯一的WorkerlD/DatacenterlD(可通过 ZookeeperConsul、数据库等协调服务分配)
    • 强依赖机器时钟:时钟不准或跳跃会影响单调递增性
  • 改进方案:

    • 关闭 NTP 同步(不推荐,时间会漂移)
    • 运行时检测到时钟回拨则等待或报错(影响可用性)
    • 扩展 WorkerlD 位数,预留一部分 ID 作为“时间回拨缓冲”(如美团 Leaf-Snowflake)
  • 变种:

    • 百度 (UidGenerator) : 基于 Snowflake , 采用数据库分配 WorkerID,优化时钟处理

    • 美团 (Leaf):

      1. Leaf-Snowflake: 解决时钟回拨(使用 Zookeeper 顺序节点持久化时间戳)

      2. Leaf-Segment: 基于数据库号段模式的优化(双 Buffer 预加载)

    • 滴滴 (Tinyid): 纯基于数据库号段模式的服务化组件。

选取规则

  • 首选Snowflake其工业级变种(Leaf-Snowflake, UidGenerator):性能最高、对数据库友好、无外部存储强依赖(除WorkerID配置)。需重点解决时钟回拨问题。适合追求高性能、低延迟的场景。
  • 次选号段模式 (DB 或 Redis):实现相对简单,性能较好。DB 号段更成熟可靠,Redis 号段需警惕持久化风险。适合中等规模、对时钟问题敏感或已有可靠 KV 存储的场景。Leaf-Segment/Tinyid是很好的服务化封装。
  • ID生成服务:适合大型公司或复杂架构,需要统一管理 ID 生成,屏蔽底层细节。底层通常还是基于 Snowflake 或号段。
  • UUID:仅适用于对存储和性能要求极低、且对顺序无要求的场景。
  • DB 简单自增:绝对避免。

雪花算法 VS 号段服务

总结 

到此这篇关于数据库分表之后id主键的处理办法的文章就介绍到这了,更多相关数据库分表id主键处理内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • 解决sql server不支持variant数据类型的问题

    解决sql server不支持variant数据类型的问题

    在数据库中,数据类型是非常重要的,但有时候我们可能会遇到 SQL Server 不支持的数据类型,例如 Variant,在本篇博文中,我们将探讨问题的背景,提供解决思路,并总结如何解决 SQL Server 不支持 Variant 数据类型的挑战
    2023-09-09
  • 解决JDBC的class.forName()问题

    解决JDBC的class.forName()问题

    这篇文章主要介绍了关于JDBC的class.forName()问题,比较两个Java文件可见,连接Db2和连接MySQL的方式非常类似,唯一的区别在于,调用 DriverManager.getConnection() 方法时,传入的URL不同,本文给大家详细讲解,需要的朋友参考下
    2022-09-09
  • 使用ORM新增数据在Mysql中的操作步骤

    使用ORM新增数据在Mysql中的操作步骤

    这篇文章主要介绍了使用ORM新增数据在Mysql中,但是在这需要注意需要大家新建ORM模型,具体搭建步骤及详细过程跟随小编一起看看吧
    2021-07-07
  • Mysql Explain 详细介绍

    Mysql Explain 详细介绍

    这篇文章主要介绍了Mysql Explain 详细介绍的相关资料,这里对Mysql Explain 的语法,属性,以及如何使用,做了全面介绍,需要的朋友可以参考下
    2016-11-11
  • MySQL数据库语句完全讲解

    MySQL数据库语句完全讲解

    MySQL作为最流行的开源关系型数据库,其查询语句涵盖了从基础数据检索到复杂多表关联的全场景需求,这篇文章主要介绍了MySQL数据库语句的相关资料,文中通过代码介绍的非常详细,需要的朋友可以参考下
    2026-01-01
  • Mysql中Cast()函数的用法案例详解

    Mysql中Cast()函数的用法案例详解

    CAST 函数是 SQL 中的一种类型转换函数,它用于将一个数据类型转换为另一个数据类型,这篇文章主要介绍了Mysql中Cast()函数的用法,需要的朋友可以参考下
    2023-05-05
  • MySQL错误:Can‘t connect to MySQL server on localhost解决办法(分多种情况)

    MySQL错误:Can‘t connect to MySQL server on localhost解决办法

    这篇文章主要给大家介绍了关于MySQL错误:Can‘t connect to MySQL server on localhost的解决办法,文中介绍的方法分多种情况,通过代码介绍的非常详细,需要的朋友可以参考下
    2024-05-05
  • mysql常用命令行操作语句

    mysql常用命令行操作语句

    MySQL很早以前只能采用DOS式界面,后来虽然硬件支持图形界面(平常的软件操作界面),但是命令行界面(就是DOS界面)以它 简单,高效,方便 的特色而被保留下来。这就是用DOS界面的原因。
    2016-05-05
  • SQL多表多字段比对方法实例代码

    SQL多表多字段比对方法实例代码

    有时候正式库和测试库同一个表有字段有差异,会造成各种错误,下面这篇文章主要给大家介绍了关于SQL多表多字段比对方法的相关资料,文中通过实例代码介绍的非常详细,需要的朋友可以参考下
    2022-05-05
  • mysql8创建、删除用户以及授权、消权操作详解

    mysql8创建、删除用户以及授权、消权操作详解

    上网找过资料说要进入mysql数据库在进行这些操作,所以下面这篇文章主要给大家介绍了关于mysql8创建、删除用户以及授权、消权操作的相关资料,文中通过示例代码介绍的非常详细,需要的朋友可以参考下
    2022-03-03

最新评论