【项目】分布式唯一ID解决方案

时间:2022-10-26 15:55:15

项目代码地址:https://gitee.com/realBeBetter/distributed-id

测试结果基于:
redis 3.2.100 Windows、jdk 1.8.0_201、mysql 8.0.22
机器配置:16G、R7-5800H、Windows 10专业版
测试配置:2000 线程,10s 启动时长,随机时间 1s ,同时并发请求访问 5 个服务

数据库序列化ID

逐个获取

首先在数据库中进行下列的初始化步骤。

DROP TABLE IF EXISTS `sequence_id`;
CREATE TABLE `sequence_id`  (
  `id` bigint UNSIGNED NOT NULL AUTO_INCREMENT,
  `value` char(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT '',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic;

我们可以使用 value 来进行记录我们申请 ID 的服务项目。
每次需要使用的时候,我们只需要向目标数据库中插入新的数据即可。

SequenceId id = new SequenceId();
id.setValue(value);
int insert = sequenceIdMapper.insert(id);
if (insert == 1) {
    return id.getId();
}

因为 Mybatis 会在进行插入操作之后完成对象数据回填,所以可以直接获取。
优缺点:

  • 可以实现序列化ID的生成,且能够记录每次请求的源服务。
  • 效率低,数据库 IO 会成为性能瓶颈。且在长时间提供服务之后,会造成数据量过大,拖累数据库效率。

【项目】分布式唯一ID解决方案

批量获取

在数据库中执行以下操作:

CREATE TABLE `id_generator`  (
  `id` int NOT NULL,
  `max_id` bigint NOT NULL COMMENT '当前最大id',
  `step` int NOT NULL COMMENT '号段的布长',
  `biz_type` int NOT NULL COMMENT '业务类型',
  `version` int NOT NULL COMMENT '版本号',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic;

INSERT INTO `id_generator` VALUES (1, 1, 2000, 1, 1);

添加了新的表格之后,我们就可以通过每次对语句执行 update 操作。

@Override
public List<Long> getGenerateId(Integer step) {
    IdGeneratorDTO maxId = idGeneratorMapper.getMaxId();
    if (Objects.isNull(maxId)) {
        idGeneratorMapper.initGenerateId();
    }
    IdGeneratorDTO currentMax = idGeneratorMapper.getMaxId();
    Integer version = currentMax.getVersion();
    Long max = currentMax.getMaxId();
    Integer id = currentMax.getId();
    IdGenerator idGenerator = new IdGenerator(id, max + step, step, 1, version + 1);
    List<Long> ids = new ArrayList<>();
    idGeneratorMapper.updateById(idGenerator);
    for (Long i = max; i < max + step; i++) {
        ids.add(i);
    }
    return ids;
}

每次执行之后,一次性返回批量的号段数据,完成 ID 的分发。这样的操作,获得的是批量的 ID 。
【项目】分布式唯一ID解决方案

Redis生成

Redis 生成 ID 的可行性,主要是依靠 redis 中的 incry 命令。
主要实现方案:

@Service
public class RedisIdServiceImpl implements RedisIdService {

    @Resource
    private RedisTemplate<String, Object> redisTemplate;

    @Override
    public synchronized Long getMaxId() {
        String key = "id";
        redisTemplate.opsForValue().setIfAbsent(key, 0);
        redisTemplate.opsForValue().increment(key);
        Object max = redisTemplate.opsForValue().get(key);
        String maxStr = String.valueOf(max);
        return Long.valueOf(maxStr);
    }
}

这样的方案设计下,可以利用 Redis 的原子性实现 ID 的获取。
【项目】分布式唯一ID解决方案

UUID

UUID 全称 Universally Unique Identifier,即通用唯一识别码。
现在目前 UUID 一共有五个实现版本:

  • 版本1:严格按照 UUID 定义的每个字段的意义来实现,使用的变量因子是时间戳+时钟序列+节点信息(Mac地址)
  • 版本2:基本和版本1一致,但是它主要是和 DCE( IBM 的一套分布式计算环境)。但是这个版本在 ietf 中也没有具体描述,反而在DCE 1.1: Authentication and Security Services这篇文档中说到了具体实现。所以这个版本现在很少使用到,并且很多地方的实现也都忽略了它。
  • 版本3:基于 name 和 namespace 的 hash 实现变量因子,版本3使用的是 md5 进行 hash 算法。
  • 版本4:使用随机或者伪随机实现变量因子。
  • 版本5:dfsg 基于 name 和 namespace 的 hash 实现变量因子,版本5使用的是 sha1 进行 hash 算法。

实现方案很简单,在 Java 中有对应的内置实现,直接调用即可生成 UUID 。

@Service
public class UUIDServiceImpl implements UUIDService {

    @Override
    public String getUUID() {
        ObjectIdGenerators.UUIDGenerator uuidGenerator = new ObjectIdGenerators.UUIDGenerator();
        UUID uuid = uuidGenerator.generateId(this);
        String s = uuid.toString();
        return s.replaceAll("-", "").toUpperCase(Locale.ROOT);
    }
}

测试之后的性能表现如下:
【项目】分布式唯一ID解决方案

Snowflake雪花

【项目】分布式唯一ID解决方案

  • 强依赖机器时间,如果时间回拨 Id 可能会重复
  • 不是严格的趋势递增,极端情况在机器时间不同步的情况下后生成的 Id 可能会小于先生成的 Id,即只能在 worker 级别保证递增
  • 服务需要保证 workerId 唯一(如果需要保证严格唯一的话会比较麻烦,简单可以基于服务 IP 跟 Port 来生成,但由于 workerId 只有 10 位,因此 workerId 可能会重复)

优点:雪花算法提供了一个很好的设计思想,雪花算法生成的 ID 是趋势递增,不依赖数据库等第三方系统,生成 ID 的性能也是非常高的,而且可以根据自身业务特性分配 bit 位,非常灵活。
缺点:雪花算法强依赖机器时钟,如果机器上时钟回拨,会导致发号重复。如果恰巧回退前生成过一些ID,而时间回退后,生成的ID就有可能重复。
在 MP 中,有对应的工具类为 IdWorker,可以直接用来雪花 ID 的生成。性能测试如下:
【项目】分布式唯一ID解决方案

Leaf

leaf 的实现中,主要使用 Leaf Snowflake 算法。
【项目】分布式唯一ID解决方案

参考文档:
分布式ID生成方案_YY迪迪的博客-CSDN博客_分布式id生成方案
雪花算法SnowFlake全方位详细解读,结合位运算的使用解读_追寻光的方向的博客-CSDN博客_雪花算法使用
Leaf 详解_yin__ren的博客-CSDN博客_leaf算法
Leaf——美团点评分布式ID生成系统
分布式唯一 ID 解析.md
GitHub - Meituan-Dianping/Leaf: Distributed ID Generate Service