浅析分布式ID生成方案

939 阅读2分钟

这是我参与11月更文挑战的第6天,活动详情查看:2021最后一次更文挑战

高并发情况下的数据库自增ID会出现重复情况,导致新增失败。 解决办法就是使用分布式ID

分布式ID的生成方式有以下几种

  • UUID
  • 数据库不同步长自增
  • 雪花算法
  • redis
  • 滴滴出品(TinyID)
  • 百度 (Uidgenerator)
  • 美团(Leaf)

常用.png

UUID

生成唯一ID,最简单的就是UUID,具有唯一性,但是结果太长了,保存在数据库中,会导致索引太大,消耗太大。

数据库不同步长自增ID

设置起始值和自增步长,就可以保证唯一ID是自增有序,而且还不会重复,缺点就是不利于扩容,增加维护成本。

  • mysql 1 配置:
set @@auto_increment_offset = 1;     -- 起始值
set @@auto_increment_increment = 2;  -- 步长
  • mysql 2 配置:
set @@auto_increment_offset = 2;     -- 起始值
set @@auto_increment_increment = 2;  -- 步长

雪花算法(Snowflake)

雪花算法(Snowflake)是twitter公司内部分布式项目采用的ID生成算法,理论上单机每秒400W+,最多每秒可以生成41亿+的ID

snow.png

分段作用说明
1bit保留
41bit时间戳,精确到毫秒可以支持69年的跨度
5bitDatacenterId可以最多支持32个节点
5bitWorkerId可以最多支持32个节点
12bit毫秒内的计数支持每个节点每毫秒产生4096个ID
  • 优点
    • ID趋势递增
    • 生成效率高,单机每秒400W+
    • 支持线性扩充
    • 稳定性高,不依赖DB等服务
  • 缺点
    • 依赖服务器时间,如果服务器时间发生回拨,可能导致生成重复ID

    • 在单机上是递增的,但是由于涉及到分布式环境,每台机器上的时钟不可能完全同步,也许有时候也会出现不是全局递增的情况

Redis生成ID

Redis可以作为集中式ID生成器,数据库的表记录最后分配的ID也是集中式ID生成器。 目前使用的ID生成器结合了这两种,实现如下:

  • redis累加器
public int getId() {
    // 获取redis累加器,累加获得id
    int id = 1;
    // 判断key是否存在
    if (stringRedisLettuceTemplate.hasKey("message:id")) {
        // 如果存在key则直接获取后进行累加
        id = Integer.valueOf(stringRedisLettuceTemplate.boundValueOps("message:id").get());
        stringRedisLettuceTemplate.opsForValue().increment("message:id", 1);
    } else {
        // 如果不存在,查询数据库最后一条,获取后+1
        id = userDao.getLastId() + 1;
		// 再次+1后存入redis
        stringRedisLettuceTemplate.opsForValue().set("message:id", String.valueOf(id + 1));
    }
    return id;
}
  • 数据库
<select id="getLastId" resultType="java.lang.Integer">
    SELECT IFNULL(id, 0) AS id
    FROM user
    ORDER BY id desc 
	limit 1
</select>

参考