MySQL中BIGINT(Long)主键耗尽后的替代方案及高性能实现咨询
替代BIGINT主键的高性能方案
针对你每日高插入量、BIGINT主键即将耗尽的场景,以下几种方案既解决ID耗尽问题,又能保证插入性能:
1. 二进制存储UUID(V4)
MySQL原生的UUID()生成的字符串(36字符)作为主键会导致索引碎片严重、插入性能差,但转成二进制存储可解决这个问题:
- 表结构设计:主键字段用
BINARY(16)类型 - 插入时:使用
UUID_TO_BIN(UUID(), 1)生成二进制UUID,第二个参数1会把UUID的时间戳部分放在二进制的前8位,让插入的ID近似递增,大幅减少索引碎片INSERT INTO new_table (id, col1, col2) VALUES (UUID_TO_BIN(UUID(), 1), 'val1', 'val2'); - 查询时:用
BIN_TO_UUID(id, 1)转回字符串UUIDSELECT BIN_TO_UUID(id, 1) AS uuid, col1 FROM new_table; - 性能优势:二进制存储仅占16字节,比字符串UUID节省一半空间;近似递增的ID让B+树索引的插入性能接近自增BIGINT,完全适配高并发插入场景。
2. 雪花算法(Snowflake)ID
雪花算法生成64位数字ID,结构为时间戳(41位)+机器ID(10位)+序列号(12位),既保证全局唯一,又天然递增:
- 表结构:主键仍用
BIGINT(和原有类型兼容,减少代码改动) - 实现方式:Java端生成雪花ID(无需依赖数据库自增),示例核心逻辑:
public class SnowflakeIdGenerator { private final long epoch = 1609459200000L; // 2021-01-01 00:00:00 private final long workerIdBits = 10L; private final long sequenceBits = 12L; private long workerId; private long sequence = 0L; private long lastTimestamp = -1L; public synchronized long nextId() { long timestamp = System.currentTimeMillis(); if (timestamp < lastTimestamp) { throw new RuntimeException("Clock moved backwards"); } if (timestamp == lastTimestamp) { sequence = (sequence + 1) & ((1 << sequenceBits) - 1); if (sequence == 0) { timestamp = waitNextMillis(lastTimestamp); } } else { sequence = 0L; } lastTimestamp = timestamp; return ((timestamp - epoch) << (workerIdBits + sequenceBits)) | (workerId << sequenceBits) | sequence; } private long waitNextMillis(long lastTimestamp) { long timestamp = System.currentTimeMillis(); while (timestamp <= lastTimestamp) { timestamp = System.currentTimeMillis(); } return timestamp; } } - 性能优势:完全在应用端生成ID,避免数据库自增锁的竞争,高并发插入性能比自增BIGINT更优;ID是数字类型,索引操作和原有BIGINT一致,无额外性能开销。
- 注意点:需处理系统时钟回拨问题,可通过配置不同机器ID避免集群内重复。
3. 分布式ID生成服务
如果是多节点集群场景,可使用成熟的分布式ID服务(如美团Leaf、百度UidGenerator):
- 核心逻辑:服务端维护ID分段,给每个应用节点分配一段ID缓存,应用从本地缓存取ID,用完再申请新段
- 表结构:主键仍用
BIGINT - 性能优势:ID生成延迟极低(本地缓存),支持超高并发;服务端保证ID全局唯一且递增,索引插入性能优异。
新表迁移的性能保障步骤
如果你选择创建新表切换主键,按以下步骤操作可避免影响业务:
- 双写阶段:修改应用代码,同时向旧表和新表写入数据,持续1-2天确保数据完全同步
- 读流量切换:逐步将读请求从旧表切到新表,验证数据一致性和查询性能
- 停止旧表写入:确认新表稳定后,停止向旧表写入,完全切换到新表
- 归档旧表:后续可将旧表数据归档到冷存储,释放数据库资源
内容的提问来源于stack exchange,提问作者dead programmer
相关产品推荐
相关产品推荐

