现在的服务基本是分布式、微服务形式的,而且大数据量也导致分库分表的产生,对于水平分表就需要保证表中 id 的全局唯一性。
对于 MySQL 而言,一个表中的主键 id 一般使用自增的方式,但是如果进行水平分表之后,多个表中会生成重复的 id 值。那么如何保证水平分表后的多张表中的 id 是全局唯一性的呢?
常用的方式有两种,一是使用雪花算法,二是使用UUID。
这两种方式都有自己的优缺点。
雪花算法有以下几个优点:
高并发分布式环境下生成不重复 id,每秒可生成百万个不重复 id。
基于时间戳,以及同一时间戳下序列号自增,基本保证 id 有序递增。
不依赖第三方库或者中间件。
算法简单,在内存中进行,效率高。
雪花算法有如下缺点:
依赖服务器时间,服务器时钟回拨时可能会生成重复 id。算法中可通过记录最后一个生成 id 时的时间戳来解决,每次生成 id 之前比较当前服务器时钟是否被回拨,避免生成重复 id。
uuid优点:
简单,代码方便。
全球唯一,在遇见数据迁移,系统数据合并,或者数据库变更等情况下,可以从容应对。
本地生成,没有网络消耗。
缺点:
不易于存储:UUID太长,16字节128位,通常以36长度的字符串表示,很多场景不适用。
信息不安全:基于MAC地址生成UUID的算法可能会造成MAC地址泄露,这个漏洞曾被用于寻找梅丽莎病毒的制作者位置。
MySQL官方有明确的建议主键要尽量越短越好[4],36个字符长度的UUID不符合要求。
对MySQL索引不利:如果作为数据库主键,在InnoDB引擎下,UUID的无序性可能会引起数据位置频繁变动,严重影响性能。
使用起来也非常简单,都在JAVA中集成了,直接创建对象调用方法就可以使用了。
//雪花算法
SnowflakeGenerator snowflakeGenerator = new SnowflakeGenerator();
//雪花算法
Snowflake snowflake = new Snowflake();
System.out.println("雪花算法生成的ID:"+snowflake.nextId());
System.out.println("生成的Id是:"+snowflakeGenerator.next());
//IdUtil工具类生成uuid
String s = IdUtil.fastSimpleUUID();
System.out.println("生成的UUID是:"+s);
结果:
雪花算法生成的ID:1768079525657333760
生成的Id是:1768079525654036480
生成的UUID是:939fefde14234ee78f4e634028c90296