雪花算法snowflake是一种优秀的分布式ID生成方案,其优点突出:它能生成全局唯一且递增的ID,确保了数据的一致性和准确性;同时,该算法灵活性强,可自定义各部分bit位,满足不同业务场景的需求;此外,雪花算法生成ID的速度快,效率高,能有效应对高并发场景,是分布式系统中不可或缺的组件。
前言分布式ID的基本特性
在分布式系统的复杂环境下,数据量的持续激增对数据库架构提出了新的挑战。传统的垂直与水平分库分表策略虽然能有效应对数据存储和扩展的问题,但却引出了一个新的需求:需要一个全局唯一标识符(ID)来标定每条数据或消息队列(MQ)中的消息。
在这种情况下,依赖数据库自身的ID自增功能显然已经无法满足需求,因此,必须设计出符合以下要求的分布式ID生成策略:
- 全局唯一性,所生成的ID必须在全局范围内是独一无二的,以确保数据的精准标识,避免任何可能的ID冲突。
- 趋势递增,鉴于多数关系型数据库管理系统(RDBMS)如MySQL的InnoDB引擎使用B树结构来存储索引数据,并依赖聚集索引进行优化,最好择有序的ID以保证高效的写入性能,因此生成的ID应具备整体递增的趋势。
- 单调递增,在特定场景下,如分布式事务的版本控制、即时通讯的增量消息传递或数据排序等,需要确保每个新生成的ID都严格大于前一个ID,从而实现数据的线性顺序增长。
- 信息安全,针对某些敏感业务,如订单处理等,生成的分布式ID必须是无规则的,以防止外部通过解析ID获取到流量信息或其他商业机密,这就需要ID生成策略在保持唯一性和递增性的同时,还要具备足够的信息隐匿性。
分布式ID常见的算法
在分布ID生成的领域,目前市面上主要流行几种算法,这些算法也是众多开源项目进行优化和实现的基础:
- UUID方式,UUID由于是在本地生成,因此具有极高的性能,然而,它生成的ID长度较长,达到16字节128位,通常需要使用字符串类型进行存储。此外,UUID是无序的,这使得它在许多场景中并不适用,尤其是在作为MySQL数据库的主键和索引时。MySQL官方建议主键应尽可能短,而对于InnoDB引擎来说,索引的无序性可能会导致数据位置频繁变动,从而严重影响性能。
- 数据库自增ID方式,这种方法的缺点是每次获取ID都需要进行数据库IO操作,给数据库带来较大压力,且性能较低。如果数据库发生宕机,对依赖其服务的外部系统将是毁灭性的打击。尽管可以通过部署数据库集群来提高可用性,但问题并未从根本上解决。
- 数据库号段算法,这是对数据库自增ID的一种优化方法。通过每次获取一个号段的值,可以大大减少与数据库的交互次数,从而显著降低数据库的压力。号段越长,性能越高。即使数据库发生宕机,只要号段未用完,系统仍能在短时间内继续提供服务。美团的Leaf和滴滴的TinyId就是采用这种算法的典型例子。
- 雪花算法,Twitter开源的snowflake算法,以时间戳、机器标识和递增序列为基础生成ID。这种算法生成的ID基本呈趋势递增,且性能很高。然而,由于它强烈依赖于机器时钟,因此需要考虑时钟回拨问题。即当机器上的时间因校正而发生倒退时,可能会导致生成的ID重复。目前,百度的uid-generator和美团的Leaf也都在使用或优化这种算法。
雪花算法snowflakesnowflake定义
Snowflake算法的原理相对直观,它负责生成一个64位(long型)的全局唯一ID,这个ID的构成包括:1位无用的符号位、41位的时间戳、10位的机器ID以及12位的序列号,除了固定的1位符号位之外,其余的三个部分都可以根据实际需求进行调整:
- 41位时间戳:这部分能够表示的时间跨度为(1L<<41)/(1000L*3600*24*365),即大约69年。
- 10位机器ID:可以唯一标识最多1024台机器,如果需要对互联网数据中心(IDC)进行划分,可以将这10位拆分为两部分,例如各5位,这样,系统就能够表示最多32个IDC,且每个IDC下可以容纳32台机器。
- 12位自增序列号:用于在同一毫秒内生成多个ID,最多可以表示2^12个不同的ID,因此,理论上,Snowflake算法能够达到的每秒查询率(QPS)约为409.6万。
snowflake优缺点
- 优点:全局唯一性、趋势递增、性能高、自定义灵活性强。
- 缺点:依赖机器时钟,存在时钟回拨问题、生成的ID包含时间信息,可能导致解析复杂性。
Java代码实现snowflake
具体实现包括不考虑时间戳回拨、考虑时间戳回拨、逻辑实现流程等。生成id的过程,就是把每一种标识(时间、机器、序列号)移到对应位置,然后相加。计算最大值的几种方式在代码中分别对每个标识的最大值做了计算。反解析ID则通过移位和按位与运算来提取时间、机器和序列号。生成id的具体步骤如下:将时间戳、机器ID和序列号按照雪花算法的规则移位和组合,形成最终的全局唯一ID。
雪花算法在实际应用中的优缺点,以及如何解决时钟回拨问题,例如通过时间戳自增彻底解决时钟回拨问题、利用缓存历史序列号缓解时钟回拨问题、等待时钟校正等方法。每种方法有其适用场景,如避免时钟回拨、缓解时钟回拨等,需要根据具体需求灵活选择。