分布式唯一id生成 - 补充中没写完

本文探讨了多种分布式唯一ID生成方案,包括Twitter的SnowFlake算法、百度的UidGenerator、美团的Leaf以及MongoDB的ObjectId。每个方案都有其特点,如SnowFlake在高并发下的重复ID风险,UidGenerator对并发限制的优化,Leaf的设计,以及ObjectId在非高并发场景的应用。文章适合需要了解分布式ID生成方案的读者。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前言

各式各样的雪花算法 - Java实现

一、推特SnowFlake

/**
 * 雪花算法
 * @author nobody
 */
public final class SnowFlake {
   
    // 起始的时间戳
    private final static long START_STAMP = 1577808000000L; //2020-01-01
    // 每一部分占用的位数,就三个
    private final static long SEQUENCE_BIT = 12; //序列号占用的位数
    private final static long MACHINE_BIT = 5; //机器标识占用的位数
    private final static long DATACENTER_BIT = 5; //数据中心占用的位数
    // 每一部分最大值
    private final static long MAX_DATACENTER_NUM = ~(-1L << DATACENTER_BIT);
    private final static long MAX_MACHINE_NUM = ~(-1L << MACHINE_BIT);
    private final static long MAX_SEQUENCE = ~(-1L << SEQUENCE_BIT);
    // 每一部分向左的位移
    private final static long MACHINE_LEFT = SEQUENCE_BIT;
    private final static long DATACENTER_LEFT = SEQUENCE_BIT + MACHINE_BIT;
    private final static long TIMESTAMP_LEFT = DATACENTER_LEFT + DATACENTER_BIT;
    private long datacenterId; //数据中心
    private long machineId; //机器标识
    private long sequence = 0L; //序列号
    private long lastStamp = -1L; //上一次时间戳

    /**
     * 分布式环境id处理
     * @param datacenterId 数据中心id
     * @param machineId    机器id
     */
    public SnowFlake(long datacenterId, long machineId) {
   
        if (datacenterId > MAX_DATACENTER_NUM || datacenterId < 0) {
   
            throw new IllegalArgumentException("datacenterId can't be greater than MAX_DATACENTER_NUM or less than 0");
        }
        if (machineId > MAX_MACHINE_NUM || machineId < 0) {
   
            throw new IllegalArgumentException("machineId can't be greater than MAX_MACHINE_NUM or less than 0");
        }
        this.datacenterId = datacenterId;
        this.machineId = machineId;
    }

    /**
     * 产生下一个id
     * @return
     */
    public synchronized long nextId() {
   
        long curryStamp = timeGen();
        if (curryStamp < lastStamp) {
   
            throw new RuntimeException("Clock moved backwards.  Refusing to generate id");
        }

        if (curryStamp == lastStamp) {
   
            // if条件里表示当前调用和上一次调用落在了相同毫秒内,只能通过第三部分,序列号自增来判断为唯一,所以+1.
            sequence = (sequence + 1) & MAX_SEQUENCE;
            //同一毫秒的序列数已经达到最大,只能等待下一个毫秒
            if (sequence == 0L) {
   
                curryStamp = getNextMill();
            }
        } else {
   
            // 不同毫秒内,序列号置为0
            // 执行到这个分支的前提是currTimestamp > lastTimestamp,说明本次调用跟上次调用对比,已经不再同一个毫秒内了,这个时候序号可以重新回置0了。
            sequence = 0L;
        }
        lastStamp = curryStamp;
        // 就是用相对毫秒数、机器ID和自增序号拼接
        return (curryStamp - START_STAMP) << TIMESTAMP_LEFT // 时间戳部分
                | datacenterId << DATACENTER_LEFT           // 数据中心部分
                | machineId << MACHINE_LEFT                 // 机器标识部分
                | sequence;                                 // 序列号部分
    }

    /**
     * 比较新旧时间戳
     * @return
     */
    private long getNextMill() {
   
        long mill = timeGen();
        while (mill <= lastStamp) {
   
            mill = timeGen();
        }
        return mill;
    }

    /**
     * 当前的时间戳
     * @return
     */
    private long timeGen() {
   
        return System.currentTimeMillis();
    }

    public static void main(String[] args) {
   
        SnowFlake snowFlake = new SnowFlake(2, 3);
        for (int i = 0; i < 10; i++) {
   
            System.out.println(snowFlake.nextId());
        }
    }

}

有一个缺点,在高并发场景下如果不稍加处理还是会出现重复的id。

二、百度UidGenerator

文章目录
GitHub - baidu/uid-generator: UniqueID generator
生成全局唯一ID之UidGenerator_三刻的博客-优快云博客_uidgenerator

基于雪花算法进行二次改进,克服了雪花算法的并发限制数问题。

三、美团Leaf

文章目录
Leaf/README_CN.md at master · Meituan-Dianping/Leaf · GitHub
Leaf——美团点评分布式ID生成系统 - 美团技术团队 (meituan.com)

四、Mongo - ObjectId

我测试了一下单机环境的毫秒级并发还是很难吃的住这个唯一性,但是非高并发场景下的唯一id是没什么问题。

import java.io.Serializable;
import java.nio.ByteBuffer;
import java.security.SecureRandom;
import java.util.Date;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * MongoDB的ObjectId对象的全局唯一标识符。
 * 由12个字节组成,划分如下:
 * 1)4字节(8位)timeStamp:UNIX时间戳(精确到秒)。
 * 2)3字节(6位)machine:所在主机的唯一标识符(这里使用随机值)。
 * 3)2字节(4位)pid:同一台机器不同的进程产生objectid的进程标识符(这里使用随机值) 。
 * 4)3字节(6位)increment:由一个随机数开始的计数器生成的自动增加的值,用来确保在同一秒内产生的objectid也不会发现冲突,允许256^3(16777216)条记录的唯一性。
 * @mongodb.driver.manual core/object-id ObjectId
 * <p>
 * 2次生成的结果为:
 * 5dbbd598 8dd521 6733 04ca5c
 * 5dbbd5a7 5350a9 3c97 3a1692
 */
public final class NewMongoUidGeneratorUtil implements Comparable<NewMongoUidGeneratorUtil>, Serializable {
   

    private static final long serialVersionUID = 3670079982654483072L;

    private static final int OBJECT_ID_LENGTH = 12;
    private static final int LOW_ORDER_THREE_BYTES = 0x00ffffff;

    // Use primitives to represent the 5-byte random value.
    private static final int RANDOM_VALUE1
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值