如何更换MySQL表的自增主键

一、问题

怎么讲MySQL表的自增主键更改为分布式ID?解决这个问题的核心在于上线时间段,会有两种id的生成方式。

为什么需要替换?数据同步的时候。假设A机房要迁移到B机房,两个机房都有流量。这里我们只讨论B机房是新机房,存量数据不会有冲突,只有增量数据才有冲突的情况。

在这里插入图片描述

二、技术点

MySQL主键值的设置有不同的方案。

2.1MySQL自增主键

MySQL自增主键的核心逻辑是通过内存计数器分配唯一递增ID,但不同版本在计数器持久化锁策略上存在关键差异,直接影响ID连续性和并发性能。

基础生成逻辑(各版本通用)

自增主键依赖表级内存计数器,分配规则如下:

  1. 自动分配:插入时未指定ID/指定为0/null,则用当前计数器值,分配后计数器+1(步长默认1,可通过auto_increment_increment调整)。
  2. 手动指定:若指定ID>当前计数器,计数器更新为ID+1;若ID≤计数器且未冲突,计数器不变。
  3. 不连续场景:事务回滚、唯一键冲突、批量插入失败会导致ID“浪费”(分配后未使用,不会回滚)。
版本差异核心对比

不同版本的关键区别集中在计数器持久化锁模式,直接影响重启后ID连续性和并发插入性能:

特性MySQL 5.7及更早MySQL 8.0及以后
计数器存储仅内存,重启后重置为max(id)+1持久化到redo log,重启后恢复原值
默认锁模式innodb_autoinc_lock_mode=1(连续模式)innodb_autoinc_lock_mode=2(交错模式)
批量插入锁策略表级锁(语句结束释放)轻量互斥锁(分配ID后立即释放)
ID连续性批量插入保证连续,简单插入可能不连续所有插入可能不连续,但并发性能最优
关键机制:锁模式

锁模式决定并发插入时ID的分配效率和连续性,各模式适用于不同场景:

锁模式值名称适用场景并发性能ID连续性
0传统模式基于语句的复制(SBR)严格连续
1连续模式混合复制(默认,平衡性能)批量连续
2交错模式基于行的复制(RBR/GTID)不保证

2.2分布式ID生成方案

雪花算法

在分布式系统中,生成全局唯一且有序的ID是关键挑战。雪花算法(Snowflake)作为Twitter开源的经典方案,通过巧妙的位结构设计,平衡了唯一性、有序性和性能需求,成为分布式ID生成的事实标准。

核心原理:64位ID的精妙结构

雪花算法将64位长整型ID划分为4个部分,每部分承担特定职责:

位数名称作用
1位符号位固定为0(确保ID为正数)
41位时间戳记录当前时间-起始时间的毫秒差,支持约69年(2^41/1000/365/24/60/60)
10位机器ID含5位数据中心ID+5位节点ID,支持最多1024台机器(2^10)
12位序列号同一毫秒内自增,支持每节点每毫秒生成4096个ID(2^12)

生成流程

  1. 时间戳:以自定义起始时间(如2020-01-01)为基准,计算当前时间偏移量;
  2. 时钟回拨处理:若当前时间 < 上次生成时间,抛出异常或等待时钟同步;
  3. 序列号生成:同一毫秒内序列号自增,溢出则等待下一毫秒;
  4. 组合ID:通过位运算拼接时间戳、机器ID和序列号,生成64位整数。
核心优势:为何成为分布式首选?
  1. 全局唯一性:时间戳+机器ID+序列号的三重组合,确保分布式环境下ID无重复,即使多节点并发也不会冲突。

  2. 高性能与低延迟:纯内存计算,无需数据库或中心节点,单机每秒可生成 400万+ ID(1000ms × 4096序列号),适合高并发场景(如秒杀、订单系统)。

  3. 趋势有序性:时间戳位于高位,ID天然按生成时间递增,适合作为数据库主键(减少索引碎片)或排序字段。

  4. 无中心依赖:去中心化设计,避免单点故障风险,支持集群横向扩展。

NT ID

公司自研的Non-Timestamp ID,非基于时间戳生成的全球唯一 ID。算是号段模式的一种,从数据库批量预分配ID段(如 [1001, 2000]),缓存在内存中逐个分配,用完后再申请新号段[4001,5000]

三、方案

对于上面的具体场景,有两种方案可以选择:

方案1:仍然使用自增主键,不同机房使用不同的数据范围方案2:使用NT ID
图示在这里插入图片描述在这里插入图片描述Q:为什么需要这么多操作?
A:代码发布过程中,如果NTID生成的值有落库,自增主键会在此数值之上增加,可能和NTID生成的值有冲突
优点1. 开发量:无需任何改造解决主键冲突问题,可在创建表或者通过ALTER TABLE 表名 AUTO_INCREMENT = 1000;修改自增主键值1. 完美的支持双向同步
缺点1. 同步方式:该方案只支持单向同步
2. 风险:需要确保A中的表自增主键不会迅速增长到B的值、确保A表中不会设置较大主键
3.全面性:除非A机房后续废弃不用,否则增量同步的数据终会出问题
1. 开发量:需要对表的创建操作设置主键值
2. 主键类型是不是bigint,防止越界
3. 通过接口提供的主键值,过长是否会有问题。如js对int64会丢失精度

最后

大家如果喜欢我的文章,可以关注我的公众号(程序员麻辣烫)

我的个人博客为:https://shidawuhen.github.io/

往期文章回顾:

  1. 设计模式
  2. 招聘
  3. 思考
  4. 存储
  5. 算法系列
  6. 读书笔记
  7. 小工具
  8. 架构
  9. 网络
  10. Go语言
MySQL 中创建带有自主键是一种常见的做法,适用于需要唯一标识每一条记录的场景。自主键通过 `AUTO_INCREMENT` 关键字实现,通常与 `INT` 类型的列一起使用,并将其设为主键(`PRIMARY KEY`)。 以下是一个创建带有自主键的示例: ```sql CREATE TABLE users ( id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY, user_name VARCHAR(15) NOT NULL ); ``` 在上述语句中: - `id` 列被定义为 `INT UNSIGNED` 类型,示无符号整数,以支持更大的数值范围。 - `NOT NULL` 约束确保该列不允许存储 `NULL` 值。 - `AUTO_INCREMENT` 关键字示该列的值会自动递。 - `PRIMARY KEY` 指定 `id` 列为主键,用于唯一标识中的每一行记录[^2]。 如果希望自的起始值不是默认的 1,可以显式指定起始值。例如,若希望 `id` 从 100 开始递,可以使用如下语句: ```sql CREATE TABLE uu ( id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY, user_name VARCHAR(15) NOT NULL ) AUTO_INCREMENT = 100; ``` 在此情况下,`AUTO_INCREMENT = 100` 示自序列的起始值为 100[^1]。 此外,还可以在定义列时直接指定 `PRIMARY KEY`,也可以在定义的最后单独指定主键。例如,以下语句定义了一个 `products` ,并将 `product_id` 列设为主键: ```sql CREATE TABLE products ( product_id INT AUTO_INCREMENT PRIMARY KEY, product_name VARCHAR(50), price DECIMAL(10, 2) ); ``` 此语句中同样使用了 `AUTO_INCREMENT` 来定义 `product_id` 列为自主键[^3]。 对于 `PRIMARY KEY` 的定义,MySQL 提供了两种方式:可以在列定义时直接指定,也可以在定义的最后单独指定主键。例如,以下语句展示了如何在定义的最后单独指定主键: ```sql CREATE TABLE ss ( id INT UNSIGNED NOT NULL AUTO_INCREMENT, user_name VARCHAR(15) NOT NULL, PRIMARY KEY (id) ); ``` 在此情况下,`PRIMARY KEY (id)` 明确指定了 `id` 列为主键[^4]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

程序员麻辣烫

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值