栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

参考 objectId 实现分布式 id

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

参考 objectId 实现分布式 id

1、参考文档

https://mongodb.github.io/mongo-java-driver/4.5/apidocs/bson/org/bson/types/ObjectId.html

http://docs.mongodb.org/manual/core/object-id

2、基本概念

objectId 是 mongoDb 开源的分布式 id 生成算法。其核心思想就是:使用12个字节生成全局唯一 id (24个16进制字符)。生成规则:

​ 4个字节:秒级别时间戳

​ 5个字节:随机数(mongo-java-driver3.4之前的版本为3个字节主机名+2个字节进程号,但在docker容器中,存在bug)

​ 3个字节:自增计数器(初始化一个随机数,然后递增)

解析:

1、先存时间戳,是为了保证分布式系统内生成的 id 趋势递增。

2、5个字节的随机数,使不同机器生成机器码碰撞概率非常低,为 1/(2的40次方)。进程启动时确定随机值,后续生成 id 时随机数不变。

3、自增计数器,自增时需要保证线程安全,确保一秒钟内能生成 2的24次方(约1600w)个不重复的id。(实际业务,单机器不会有这么高的并发量的。)

优点:

1、不用依赖于其它组件分配机器码。

2、本地生成,高性能,id 趋势递增。

3、时间回拨或闰秒基本没影响(时间回拨基本是50ms以内,1600w的自增id足以应付,而且时间回拨是机器配置为ntpdate才有可能出现,概率极低,出现了是运维的问题)

4、算法有效期为 2的32次方-当前秒数,当前约剩余86年,如果设置系统起始时间,算法有效期可到达 138年。

缺点:

1、对比雪花算法,自增id,存储空间大

2、由于长度和字符比较问题,数据库检索、构建索引的效率比雪花算法低一些(自测1000w数据插入、统计,耗时 雪花2:mongo 3)

3、机器码碰撞概率很低,但还有存在这种概率的。但默认不会碰撞,道理和uuid一样。

4、自增id重置时,同一秒内的id不是递增趋势。自增id会泄漏运营情况。1s生成超过1600w个id时,会出现重复id。

综合而言,objectId 比雪花算法id更适合微服务体系,不用考虑机器码分配问题,可部署机器更多,同时满足趋势递增的要求。

3、源码

参考标准生成规则,使用 Java 实现 objectId 生成:

import java.util.Random;
import java.util.concurrent.ThreadLocalRandom;
import java.util.concurrent.atomic.AtomicInteger;


final class ObjectId {
    private static final char[] HEX_UNIT = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
    
    private final static long SYSTEM_START_TIME = 1646064000L;
    
    private static final AtomicInteger SEQUENCE_COUNTER = new AtomicInteger(ThreadLocalRandom.current().nextInt());
    private static final char[] MACHINE_CODE = initMachineCode();

    
    public static String next() {
        char[] ids = new char[24];
        int epoch = (int) ((System.currentTimeMillis() / 1000) - SYSTEM_START_TIME);
        // 4位字节 : 时间戳
        for (int i = 7; i >= 0; i--) {
            ids[i] = HEX_UNIT[(epoch & 15)];
            epoch >>>= 4;
        }
        // 5位字节 : 随机数
        System.arraycopy(MACHINE_CODE, 0, ids, 8, 10);
        // 3位字节: 自增序列。溢出后,相当于从0开始算。
        int seq = SEQUENCE_COUNTER.incrementAndGet();
        for (int i = 23; i >= 18; i--) {
            ids[i] = HEX_UNIT[(seq & 15)];
            seq >>>= 4;
        }
        return new String(ids);
    }

    private static char[] initMachineCode() {
        char[] macAndPid = new char[10];
        Random random = new Random();
        for (int i = 9; i >= 0; i--) {
            macAndPid[i] = HEX_UNIT[random.nextInt() & 15];
        }
        return macAndPid;
    }

}
转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/754468.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号