美文网首页
分布式ID

分布式ID

作者: 贪挽懒月 | 来源:发表于2021-08-14 18:36 被阅读0次

    1. 是什么?

    分布式 ID 就是在分布式项目中我们给数据库记录用的 ID。和单机版项目有啥不同呢?单机版的我们可以用 数据库自增等方式来生成 ID,但是分布式项目中,项目部署在好几台机器上,数据库自增也是有可能会出现重复的情况。所以就需要一种算法来生成适用于分布式系统的 ID。

    2. 生成分布式 ID 的算法要求:

    • 全局唯一:生成的 ID 必须全局唯一;
    • 趋势递增:我们应该尽量选择有序的主键来保证索引的性能;
    • 单调递增:尽量保证下一个的 ID 大于上一个;
    • 信息安全:如果是连续的 ID,攻击者很容易就猜出下一条记录的 ID,所以有些情况下尽量让 ID 无规则;
    • 含时间戳:含时间戳便于追踪。

    3. 生成分布式 ID 系统的可用性要求:

    我们用一个系统来生成分布式 ID,那么这个系统必须符合以下条件:

    • 高可用:要保证在99.999%的情况下能够生成一个唯一的分布式 ID;
    • 低延迟:生成分布式 ID 的速度一定要快;
    • 高QPS:假如一下子来10w个生成分布式 ID 的请求,服务器要能扛得住并且能够一下子生成10w个。

    4. 分布式 ID 的生成方案:

    • UUID:包含32个16进制的数字,以连字符分割成五段,格式是8-4-4-4-12。优点是性能好,本地生成,没有网络消耗。缺点是它无序,不能生成递增的 ID,而且很长,入库性能差,因为 MySQL的 是 B+ 树索引,每插入一条新数据,都会对索引进行改造,因为 UUID 的无序,每次插入数据时 B+ 树的改造就会很大,也就是导致索引分裂。

    • 数据库自增:我们可以专门搞个表,利用 MySQL 的replace into 语句来生成 ID。比如创建一个表:

    create table t_test(
        id bigint(20) unsigned not null auto_increment primary key,
        col char(1) not null default '',
        unique key col (col)
    )
    

    然后我们可以执行语句:

    replace into t_test (col) values('a');
    

    每执行一次,t_test 表的 id 字段值就会自增,我们就可以用这个 id 来做分布式 ID。这个方案对于并发量不高的系统足够了,但是并发量高的话还是不行的。首先用来生成 ID 的这个 MySQL 扛不住高并发,一秒钟生成10w个肯定是做不到的。

    • 使用 redis 生成分布式 ID:因为 redis 的命令是原子操作的,所以可以使用 incr 和 incrby 来生成分布式 ID。但是这个方案也不是很完美,因为 redis 是集群的话,我们同样需要设置不同的自增步长,同时 key 要设置过期时间。集群有多少台,步长这么设置这些都是要考虑的。而且为了一个 ID 要部署和维护一套 redis 的集群,成本偏高。

    所以以上三种方案都存在一定的缺点,现在比较流行的是用雪花算法。

    5. 雪花算法:

    雪花算法是推特开源的一套用于生成分布式 ID 的算法。它可以生成一个 64bit 大小的整数,类型是 Long,转成字符串后最长是19位。

    (1). 雪花算法的组成:

    0         0000000000 0000000000 0000000000 0000000000 0         0000000000      0000000000 00
    1bit符号位                这 41 bit 是时间戳                  10 bit 工作进程位     12bit 序列号位
    

    1 + 41 + 10 + 12 的结构,总共 64bit,所以刚好可以对应 java 的 long 类型,所以雪花算法生成的 id 就用 long 类型存储。

    • 符号位永远是0,0表示整,1表示负,我们生成的 id 肯定不希望是负的;
    • 时间戳是41位,假如全都是1,那就是2的41次方减1,该值是毫秒,换算成年就是69.73年,所以说雪花算法可以用大约69年,从1970开始,可以用到2039年。
    • 工作进程位是10bit,并且这10bit有5bit是机房号,5bit是机器编号,2的5次方是32,所以就支持32个机房,每个机房32台机器,总共就支持1024个节点。
    • 序列号位是12bit,用来记录同毫秒内产生的不同 id。2的12次方减1是4095,表示同一机器在同一毫秒内可以生成4095个 ID。

    (2). 怎么用?

    hutool 工具包已经给我们封装好了,只需要在项目中引入:

    <!-- https://mvnrepository.com/artifact/cn.hutool/hutool-captcha -->
    <dependency>
        <groupId>cn.hutool</groupId>
        <artifactId>hutool-captcha</artifactId>
        <version>5.6.6</version>
    </dependency>
    

    然后通过如下方式即可获取到雪花算法生成的 ID:

    public class SnowFlakeUtil {
    
        public static long snowFlakeId(long workerId, long datacenterId){
            Snowflake snowflake = IdUtil.getSnowflake(workerId, datacenterId);
            return snowflake.nextId();
        }
    
        public static void main(String[] args){
            ExecutorService service = Executors.newFixedThreadPool(5);
            // 机器的workerId
            long workerId = 1;
            // 机房ID
            long datacenterId = 1;
            for (int i = 0; i < 20; i++) {
                service.submit(() ->{
                    System.out.println(snowFlakeId(workerId, datacenterId));
                });
            }
            service.shutdown();
        }
    }
    

    (3). 雪花算法优缺点:

    优点是简单易用,有序递增,带时间戳,也满足信息安全。缺点也有,就是依赖机器时钟,可能会有时钟回拨问题。如果两台服务器的时间不同步,可能会导致生成重复的 ID。

    (4). 雪花算法的优化:

    百度开源的 UidGenerator 和美团开源的 Leaf 就解决了时钟回拨问题。

    相关文章

      网友评论

          本文标题:分布式ID

          本文链接:https://www.haomeiwen.com/subject/fgpqeltx.html