分布式场景下你还在用UUID吗,shardingjdbc分库分表的ID你又是怎么设置的?雪花ID你了解多少

我们在开发过程中,单库下一般使用Mysql自增ID, 但是分库分表后,会造成不同分片上的数据表主键的重复

现在有个需求:表ID需要性能强劲且全局唯一并且能防止恶意用户根据id的规则来获取数据

下面来看看业界常用的ID解决方案:

一、数据库自增ID

利用自增id, 设置不同的自增步长,auto_increment_offset、auto-increment-increment

DB1: 单数
//从1开始、每次加2
DB2: 偶数
//从2开始,每次加2

缺点:

依靠数据库系统的功能实现,但是未来扩容麻烦
主从切换时的不一致可能会导致重复发号
性能瓶颈存在单台sql上

二、UUID

优点:性能非常高,没有网络消耗

缺点:

无序的字符串,不具备趋势自增特性
UUID太长,不易于存储,浪费存储空间,很多场景不适用

三、Redis发号器

利用Redis的INCR和INCRBY来实现,原子操作,线程安全,性能比Mysql强劲

缺点:需要占用网络资源,增加系统复杂度

四、Snowflake雪花算法

优点:
twitter 开源的分布式 ID 生成算法,代码实现简单、不占用宽带、数据迁移不受影响
生成的 id 中包含有时间戳,所以生成的 id 按照时间递增
部署了多台服务器,需要保证系统时间一样,机器编号不一样
缺点
依赖系统时钟(多台服务器时间一定要一样)

具体的选择,根据业务情况和公司服务器情况来定,这里重点介绍shardingjdbc中使用雪花算法,以及坑的解决

什么是雪花算法呢? 

twitter用scala语言编写的高效生成唯一ID的算法,生成的ID不重复,算法性能高,基于时间戳,基本保证有序递增
雪花算法生成的数字,long类,所以就是8个byte,64bit

表示的值 -9223372036854775808(-2的63次方) ~ 9223372036854775807(2的63次方-1)
生成的唯一值用于数据库主键,不能是负数,所以值为0~9223372036854775807(2的63次方-1)

一般来说,分布式ID的生成需求是 性能强劲,全局唯一不能重复,防止恶意用户根据id的规则来获取数据

其中,全局唯一不能重复-请注意 有坑 

坑一:分布式部署就需要分配不同的workId, 如果workId相同,可能会导致生成的id相同

坑二:分布式情况下,需要保证各个系统时间一致,如果服务器的时钟回拨,就会导致生成的 id 重复

使用方式一:

订单id使用MybatisPlus的配置,TrafficDO类配置

@TableId(value = "id", type = IdType.ASSIGN_ID)
默认实现类为DefaultIdentifierGenerator雪花算法


使用方式二: 

 使用Sharding-Jdbc配置文件,注释DO类里面的id分配策略 

#id生成策略
          key-generator:
            column: id
            props:
              worker:
                id: 0
            #id生成策略
            type: SNOWFLAKE

 一般使用第二种,但是需要动态指定sharding jdbc 的雪花算法中的属性work.id属性

坑一的解决方式:

动态指定方式是启动程序的时候,通过JVM参数去控制,覆盖变量 

@Configuration
public class SnowFlakeWordIdConfig {


    /**
     * 动态指定sharding jdbc 的雪花算法中的属性work.id属性
     * 通过调用System.setProperty()的方式实现,可用容器的 id 或者机器标识位
     * workId最大值 1L << 100,就是1024,即 0<= workId < 1024
     * {@link SnowflakeShardingKeyGenerator#getWorkerId()}
     *
     */
    static {
        try {
            InetAddress ip4 = Inet4Address.getLocalHost();
            String addressIp = ip4.getHostAddress();
            System.setProperty("workerId", (Math.abs(addressIp.hashCode())%1024)+"");
        } catch (UnknownHostException e) {
            throw new BizException(BizCodeEnum.OPS_NETWORK_ADDRESS_ERROR);
        }
    }
}

 再更改下上面的配置,动态获取workerId

#id生成策略
          key-generator:
            column: id
            props:
              worker:
                id: ${workerId}
            #id生成策略
            type: SNOWFLAKE

坑二的解决方式:

shardingjdbc-4.1版本中做了相关操作,看源码:

当判断到时间有差距,且在容忍范围内,就等待时间同步到最后一次id生成的时间再进行。

超过容忍度,则直接抛出异常clock is moving backwards,last time is ...

 

おすすめ

転載: blog.csdn.net/wnn654321/article/details/122506802