为什么使用雪花算法?❄️

 在复杂的系统中,往往需要对大量的数据如订单,账户进行标识,以一个有意义的有序的序列号来作为全局唯一的ID,即需要一个ID号生成器

 ID号生成器是服务端系统的基础设施,而且ID号这个东西基本搞后端开发的程序员天天都要接触。而关于ID生成的算法现在业界首屈一指的当属Snowflake雪花算法。

常见企业级使用的方案

1. UUID

UUID = 4个连字号(-) + 32个字节长的字符串,总共36个字节长。比如:

550e8400-e29b-41d4-a716-446655440000

优点:

  1. 能够保证 独立性
  2. 程序可以在不同的 数据库间迁移,效果不受影响。

缺点:

  1. 不易于存储 / 性能问题:UUID太长,128位、36长度字符,不利于Mysql索引(在InnoDB引擎下,UUID的无序性 可能会引起数据位置频繁变动,严重影响性能),很多场景不适用。
  2. 采用无意义字符串,数据量增大时造成访问过慢,且不宜排序
  3. 信息不安全:基于MAC地址生成UUID的算法可能会造成MAC地址泄露,这个漏洞曾被用于寻找梅丽莎病毒的制作者位置。

2. 基于数据库方案

自增ID:对于数据敏感场景不宜使用,且不适合于分布式场景

优点:

  1. 简单,成本小
  2. ID号单调自增,可以实现一些对ID有特殊要求的业务。

缺点:

  1. 不同数据库语法和实现不同,数据库迁移的时候或多数据库版本支持的时候需要处理。
  2. 如果涉及多个系统需要合并或者数据迁移会比较麻烦。

3. 雪花算法

snowflake-64bit
算法描述:

  • 最高位是符号位,始终为0,不可用。
  • 41位的时间序列,精确到毫秒级,41位的长度可以使用69年。时间位还有一个很重要的作用是可以根据时间进行排序
  • 10位的机器 标识,10位的长度最多支持部署1024个节点。
  • 12位的计数序列号,序列号即一系列的自增id,可以支持同一节点同一毫秒生成多个ID序号,12位的计数序列号支持每个节点每毫秒产生4096个ID序号。

UidGenerator是百度开源的一款基于Snowflake雪花算法实现的高性能唯一ID生成器。以下是再进行封装的一套更利于Spring Boot项目使用的ID号生成组件,命名为id-spring-boot-starter。

参考

在 Spring Boot 项目中的用法

  • 导入SQL脚本
1
2
3
4
5
6
7
8
9
10
11
12
13
DROP TABLE IF EXISTS WORKER_NODE;
CREATE TABLE WORKER_NODE
(
ID BIGINT NOT NULL AUTO_INCREMENT COMMENT 'auto increment id',
HOST_NAME VARCHAR(64) NOT NULL COMMENT 'host name',
PORT VARCHAR(64) NOT NULL COMMENT 'port',
TYPE INT NOT NULL COMMENT 'node type: ACTUAL or CONTAINER',
LAUNCH_DATE DATE NOT NULL COMMENT 'launch date',
MODIFIED TIMESTAMP NOT NULL COMMENT 'modified time',
CREATED TIMESTAMP NOT NULL COMMENT 'created time',
PRIMARY KEY(ID)
)
COMMENT='DB WorkerID Assigner for UID Generator',ENGINE = INNODB;

UidGenerator需要数据库支持

  • pom中加入依赖
1
2
3
4
5
<dependency>
<groupId>cn.codesheep</groupId>
<artifactId>id-spring-boot-starter</artifactId>
<version>1.0.0</version>
</dependency>
  • 配置数据库连接
1
2
3
url: jdbc:mysql://xxx.xxx.xxx.xxx:3306/demo?useUnicode=true&characterEncoding=utf8&autoReconnect=true&useOldAliasMetadataBehavior=true&connectionCollation=utf8mb4_unicode_ci&rewriteBatchedStatements=true&allowMultiQueries=true
username: xxxxxx
password: xxxxxx
  • 修改 Spring Boot 主类

Spring Boot应用主类上加上mybatis注解即可:

1
2
3
4
5
6
7
8
9
10
11
@RestController
public class TestController {

@Autowired
private UidGenService uidGenService;

@GetMapping("/uid")
public String genUid() {
return String.valueOf("本次生成的唯一ID号为:"+uidGenService.getUid());
}
}
  1. 首先用 Autowired的方式引入UidGenService类;
  2. 直接调用UidGenService类的getUid()方法即可获得一个Long型的ID号

 为什么叫雪花算法呢?私以为众所周知世界上没有一对相同的雪花,而雪花形成的过程中复杂的环境条件则对应了雪花算法中的机器ID,时间戳在现实层面上能够精确到普朗克时间10^-43s,这样时间戳的长度也得以保证。

参考