分布式ID
日常开发中,数据需要使用唯一的ID来标识,一般情况下使用自增主键作为数据ID。但是在大数量的情况下,往往会引入分库分表,这时自增ID已经无法满足需求了,就需要一个能够生成全局唯一ID的系统。
一些常见的分布式ID生成策略有:UUID、雪花算法(Twitter的Snowflake)、基于Redis的ID生成方案等。具体选择哪种方案,需要根据系统的实际需求来决定。
UUID
UUID全称:Universally Unique Identifier,即通用唯一识别码。
UUID是由一组32位数的16进制数字所构成,是故UUID理论上的总数为16^32 = 2^128,约等于3.4 x 10^38。也就是说若每纳秒产生1兆个UUID,要花100亿年才会将所有UUID用完。
UUID的标准型式包含32个16进制数字,以连字号分为五段,形式为8-4-4-4-12的32个字符,
如:550e8400-e29b-41d4-a716-446655440000。
JAVA中生成一个UUID:
java
System.out.println(UUID.randomUUID().toString());
优点
- 全局唯一,重复率低
- 无需中央协调机制
- 安全性高
缺点
- 占用空间大
- 不易读懂
- 不适合顺序访问
- 算法复杂影响性能
雪花算法
雪花算法是Twitter开源的一种生成全局唯一的ID的算法,SnowFlake中文意思为雪花,故称为雪花算法,它主要用于在分布式系统中生成唯一ID,保证数据整体有序。雪花算法的核心思想是通过使用一个64位的整数来表示最终生成的唯一ID,这个整数由以下几个部分组成:
- 符号位(1 bit):雪花算法中符号位一直为0,表示生成的ID为正数。
- 时间戳(41 bits):时间戳部分占用了ID的高位41个二进制位。这个时间戳记录的是自定义起始时间点(如对于Twitter而言是"2010年11月4日8:43:00",对于美团而言是"2015年1月1日")到现在的毫秒数,最多可以支持69年的时间戳。因此,雪花算法生成的ID可按照时间有序递增或递减,便于存储和索引。
- 机器ID(10 bits):机器ID部分占用了ID的中间10个二进制位,包括5位datacenterId和5位workerId。它可以指定不同的机器节点,也就是可以支持1024台不同的机器。
- 序列号(12 bits):序列号部分占用了ID的最后12个二进制位。它表示同一毫秒内不同的ID序列号,每毫秒可以生成4096个序列号。所以,如果同一机器同一毫秒内请求并发量超过了4096次,会出现序列号重复的情况。
- 总计64位:这个ID总共占据64个二进制位,如果把它转成16进制,就是一个16个字符长度的字符串。这个64位的ID可以被用于全局唯一标识分布式系统中的每一个元素。
雪花算法的实现原理相对简单,不需要依赖网络和第三方存储系统,也不存在单点故障问题。同时,由于它生成的ID是全局唯一且有序的,非常适合在分布式系统中进行唯一性约束,比如数据库表的主键等场景。但是,在使用雪花算法时需要注意,如果某些节点的时钟不够同步,可能导致生成的ID不是严格有序的,因此需要保证各个节点的时钟同步。
优点
- 生成效率高
- 不重复
- 算法简单
缺点
- 依赖系统时钟,会出现时钟回拨问题。
- 单机递增,但是分布式不递增。
时钟回拨
时间回拨就是服务器上的时间突然倒退到之前的时间。
解决办法:
由于强依赖时钟,对时间的要求比较敏感,在机器工作时NTP同步也会造成秒级别的回退,建议可以直接关闭NTP同步。要么在时钟回拨的时候直接不提供服务直接返回ERROR_CODE,等时钟追上即可。或者做一层重试,然后上报报警系统,更或者是发现有时钟回拨之后自动摘除本身节点并报警