分库分表后如何生成全局唯一且趋势递增的主键?
学完这篇教程,你将掌握在分库分表场景下解决 ID 冲突和索引性能问题的核心方案,学会使用雪花算法和 Redis INCR 等方法生成全局唯一且趋势递增的主键。
第一步:使用数据库自增 ID(最简单直接)
这一步要做什么:在 MySQL 等数据库表中直接设置主键为自增模式,利用数据库自身的计数机制生成 ID。
具体操作:登录 MySQL 客户端,执行 CREATE TABLE user (id INT NOT NULL AUTO_INCREMENT, name VARCHAR(50), PRIMARY KEY (id)) ENGINE=InnoDB;。插入数据时不需要手动指定 id,只需执行 INSERT INTO user (name) VALUES ('Tom');,数据库会自动分配一个连续递增的整数。
得到结果:获得一个全局唯一的整数 ID,且 ID 是严格递增的。
注意:这是最简单的方案,但缺点是 ID 全局不唯一(不同库的自增 ID 会重复),且在分库分表后,ID 趋势递增会变成局部递增,会导致后续的 B+ 树索引频繁页分裂,写入性能急剧下降。
第二步:使用 UUID(保证全局唯一但性能差)
这一步要做什么:利用 UUID 算法生成 32 位的十六进制字符串作为主键。
具体操作:在 Java 代码中调用 String uuid = UUID.randomUUID().toString();,或者在 MySQL 中直接使用 SELECT UUID(); 函数。
得到结果:获得一个全局唯一的字符串 ID,绝对不会重复。
注意:UUID 是无序的,存入数据库后会导致 B+ 树频繁的页分裂,写入性能极差,且占用存储空间较大(36 字符),不适合作为主键。
第三步:使用雪花算法(推荐方案)
这一步要做什么:利用 Twitter 开源的雪花算法,通过时间戳、机器 ID 和序列号组合生成 64 位长整型 ID。
具体操作:编写 Java 代码,定义一个包含 workerId(机器 ID,0-31)和 datacenterId(数据中心 ID,0-31)的类,利用 System.currentTimeMillis() 获取当前时间戳,结合位运算拼接 ID。例如:long id = (timestamp << 22) | (workerId << 12) | sequence;。
得到结果:获得一个既全局唯一又趋势递增的长整型 ID,解决了分布式环境下的 ID 冲突问题,且对数据库索引友好。
注意:雪花算法依赖系统时钟,如果服务器时间回拨(如同步时间),会导致生成的 ID 重复或报错,生产环境必须做好时钟校验。
第四步:使用 Redis INCR(高并发场景)
这一步要做什么:利用 Redis 的原子性自增命令,在内存中维护一个计数器,每次获取 ID 后自增 1。
具体操作:连接 Redis,执行 INCR user_id 命令,将返回值作为主键 ID,并设置过期时间(如 24 小时)防止内存泄漏。
得到结果:获得一个全局唯一且严格递增的数字 ID,性能极高。
注意:Redis 是单线程的,性能极高,但它是单点服务,如果 Redis 宕机,服务将无法生成 ID,通常需要引入 Redis 集群或 Sentinel 模式来保证高可用。
小结:分库分表主键生成需满足唯一性和趋势递增两个核心需求。自增 ID 简单但冲突;UUID 唯一但无序影响性能;雪花算法是目前的最佳实践;Redis INCR 适合高并发但对单点故障敏感。
转载请注明出处,版权归原作者所有。