基于雪花算法(Snowflake)生产唯一分布式id

1.什么是雪花算法

snowflake是Twitter开源的分布式ID生成算法,结果是一个long型的ID。

这种方案大致来说是一种以划分命名空间(UUID也算,由于比较常见,所以单独分析)来生成ID的一种算法,这种方案把64-bit分别划分成多段,分开来标示机器、时间等。

Snowflake生成的是Long类型的ID,一个Long类型占8个字节,每个字节占8比特,也就是说一个Long类型占64个比特。
Snowflake ID组成结构:正数位(占1比特)+ 时间戳(占41比特)+ 机器ID(占5比特)+ 数据中心(占5比特)+ 自增值(占12比特),总共64比特组成的一个Long类型

2.雪花算法具体代码

/**
 * @description: 
 * @author: bubao
 */
class Genid {
    /**
     *Creates an instance of Genid.
     * @author bubao
     * @param {{
     *     Method: 1, // 雪花计算方法,(1-漂移算法|2-传统算法),默认 1
     *     BaseTime: 1577836800000,  // 基础时间(ms 单位),不能超过当前系统时间
     *     WorkerId: Number, // 机器码,必须由外部设定,最大值 2^WorkerIdBitLength-1
     *     WorkerIdBitLength: 6,   // 机器码位长,默认值 6,取值范围 [1, 15](要求:序列数位长+机器码位长不超过 22)
     *     SeqBitLength: 6,   // 序列数位长,默认值 6,取值范围 [3, 21](要求:序列数位长+机器码位长不超过 22)
     *     MaxSeqNumber: 5, // 最大序列数(含),设置范围 [MinSeqNumber, 2^SeqBitLength-1],默认值 0,表示最大序列数取最大值(2^SeqBitLength-1])
     *     MinSeqNumber: 5, // 最小序列数(含),默认值 5,取值范围 [5, MaxSeqNumber],每毫秒的前 5 个序列数对应编号 0-4 是保留位,其中 1-4 是时间回拨相应预留位,0 是手工新值预留位
     *     TopOverCostCount: 2000// 最大漂移次数(含),默认 2000,推荐范围 500-10000(与计算能力有关)
     * }} options
     * @memberof Genid
     */
    constructor(options) {
        if (options.WorkerId === undefined) {
            throw new Error("lost WorkerId");
        }
        // 1.BaseTime
        const BaseTime = 1577836800000;
        if (!options.BaseTime || options.BaseTime < 0) {
            options.BaseTime = BaseTime;
        }
        // 2.WorkerIdBitLength
        const WorkerIdBitLength = 6;
        if (!options.WorkerIdBitLength || options.WorkerIdBitLength < 0) {
            options.WorkerIdBitLength = WorkerIdBitLength;
        }

        // 4.SeqBitLength
        const SeqBitLength = 6;
        if (!options.SeqBitLength || options.SeqBitLength < 0) {
            options.SeqBitLength = SeqBitLength;
        }
        // 5.MaxSeqNumber
        const MaxSeqNumber = (1 << options.SeqBitLength) - 1;
        if (options.MaxSeqNumber <= 0 || options.MaxSeqNumber === undefined) {
            options.MaxSeqNumber = MaxSeqNumber;
        }
        // 6.MinSeqNumber
        const MinSeqNumber = 5;
        if (!options.MinSeqNumber || options.MinSeqNumber < 0) {
            options.MinSeqNumber = MinSeqNumber;
        }
        // 7.Others
        const topOverCostCount = 2000;
        if (!options.TopOverCostCount || options.TopOverCostCount < 0) {
            options.TopOverCostCount = topOverCostCount;
        }

        if (options.Method !== 2) {
            options.Method = 1;
        } else {
            options.Method = 2;
        }

        this.Method = BigInt(options.Method);
        this.BaseTime = BigInt(options.BaseTime);
        this.WorkerId = BigInt(options.WorkerId);
        this.WorkerIdBitLength = BigInt(options.WorkerIdBitLength);
        this.SeqBitLength = BigInt(options.SeqBitLength);
        this.MaxSeqNumber = BigInt(options.MaxSeqNumber);
        this.MinSeqNumber = BigInt(options.MinSeqNumber);
        this.TopOverCostCount = BigInt(options.TopOverCostCount);

        const timestampShift = this.WorkerIdBitLength + this.SeqBitLength;
        const currentSeqNumber = this.MinSeqNumber;

        this._TimestampShift = timestampShift;
        this._CurrentSeqNumber = currentSeqNumber;

        this._LastTimeTick = 0;
        this._TurnBackTimeTick = 0;
        this._TurnBackIndex = 0;
        this._IsOverCost = false;
        this._OverCostCountInOneTerm = 0;
    }

    // DoGenIDAction .
    DoGenIdAction(OverCostActionArg) { }

    BeginOverCostAction(useTimeTick) { }

    EndOverCostAction(useTimeTick) {
        // if m1._TermIndex > 10000 {
        //     m1._TermIndex = 0
        // }
    }

    BeginTurnBackAction(useTimeTick) { }

    EndTurnBackAction(useTimeTick) { }

    NextOverCostId() {
        const currentTimeTick = this.GetCurrentTimeTick();
        if (currentTimeTick > this._LastTimeTick) {
            // this.EndOverCostAction(currentTimeTick)
            this._LastTimeTick = currentTimeTick;
            this._CurrentSeqNumber = this.MinSeqNumber;
            this._IsOverCost = false;
            this._OverCostCountInOneTerm = 0;
            // this._GenCountInOneTerm = 0
            return this.CalcId(this._LastTimeTick);
        }
        if (this._OverCostCountInOneTerm >= this.TopOverCostCount) {
            // this.EndOverCostAction(currentTimeTick)
            this._LastTimeTick = this.GetNextTimeTick();
            this._CurrentSeqNumber = this.MinSeqNumber;
            this._IsOverCost = false;
            this._OverCostCountInOneTerm = 0;
            // this._GenCountInOneTerm = 0
            return this.CalcId(this._LastTimeTick);
        }
        if (this._CurrentSeqNumber > this.MaxSeqNumber) {
            this._LastTimeTick++;
            this._CurrentSeqNumber = this.MinSeqNumber;
            this._IsOverCost = true;
            this._OverCostCountInOneTerm++;
            // this._GenCountInOneTerm++

            return this.CalcId(this._LastTimeTick);
        }

        // this._GenCountInOneTerm++
        return this.CalcId(this._LastTimeTick);
    }

    NextNormalId() {
        const currentTimeTick = this.GetCurrentTimeTick();
        if (currentTimeTick < this._LastTimeTick) {
            if (this._TurnBackTimeTick < 1) {
                this._TurnBackTimeTick = this._LastTimeTick - 1;
                this._TurnBackIndex++;
                // 每毫秒序列数的前 5 位是预留位,0 用于手工新值,1-4 是时间回拨次序
                // 支持 4 次回拨次序(避免回拨重叠导致 ID 重复),可无限次回拨(次序循环使用)。
                if (this._TurnBackIndex > 4) {
                    this._TurnBackIndex = 1;
                }
                this.BeginTurnBackAction(this._TurnBackTimeTick);
            }
            
            return this.CalcTurnBackId(this._TurnBackTimeTick);
        }
        
        // 时间追平时,_TurnBackTimeTick 清零
        if (this._TurnBackTimeTick > 0) {
            this.EndTurnBackAction(this._TurnBackTimeTick);
            this._TurnBackTimeTick = 0;
        }

        if (currentTimeTick > this._LastTimeTick) {
            this._LastTimeTick = currentTimeTick;
            this._CurrentSeqNumber = this.MinSeqNumber;
            return this.CalcId(this._LastTimeTick);
        }

        if (this._CurrentSeqNumber > this.MaxSeqNumber) {
            this.BeginOverCostAction(currentTimeTick);
            // this._TermIndex++
            this._LastTimeTick++;
            this._CurrentSeqNumber = this.MinSeqNumber;
            this._IsOverCost = true;
            this._OverCostCountInOneTerm = 1;
            // this._GenCountInOneTerm = 1

            return this.CalcId(this._LastTimeTick);
        }

        return this.CalcId(this._LastTimeTick);
    }

    CalcId(useTimeTick) {
        const result = BigInt(useTimeTick << this._TimestampShift) + BigInt(this.WorkerId << this.SeqBitLength) + BigInt(this._CurrentSeqNumber);
        this._CurrentSeqNumber++;
        return result;
    }

    CalcTurnBackId(useTimeTick) {
        const result = BigInt(useTimeTick << this._TimestampShift) + BigInt(this.WorkerId << this.SeqBitLength) + BigInt(this._TurnBackIndex);
        this._TurnBackTimeTick--;
        return result;
    }

    GetCurrentTimeTick() {
        const millis = BigInt((new Date()).valueOf());
        return millis - this.BaseTime;
    }

    GetNextTimeTick() {
        let tempTimeTicker = this.GetCurrentTimeTick();
        while (tempTimeTicker <= this._LastTimeTick) {
            tempTimeTicker = this.GetCurrentTimeTick();
        }
        return tempTimeTicker;
    }


    /**
     * 生成ID
     * @returns 始终输出number类型,超过时throw error
     */
    NextNumber() {
        if (this._IsOverCost) {
            //
            let id = this.NextOverCostId()
            if (id >= 9007199254740992n)
                throw Error(`${id.toString()} over max of Number 9007199254740992`)

            return parseInt(id.toString())
        } else {
            //
            let id = this.NextNormalId()
            if (id >= 9007199254740992n)
                throw Error(`${id.toString()} over max of Number 9007199254740992`)

            return parseInt(id.toString())
        }
    }

    /**
     * 生成ID
     * @returns 根据输出数值判断,小于number最大值时输出number类型,大于时输出bigint
     */
    NextId() {
        if (this._IsOverCost) {
            let id = this.NextOverCostId()
            if (id >= 9007199254740992n)
                return id
            else
                return parseInt(id)
        } else {
            let id = this.NextNormalId()
            if (id >= 9007199254740992n)
                return id
            else
                return parseInt(id)
        }
    }

    /**
     * 生成ID
     * @returns 始终输出bigint类型
     */
    NextBigId() {
        if (this._IsOverCost) {
            //
            return this.NextOverCostId()
        } else {
            //
            return this.NextNormalId()
        }
    }
}

module.exports = Genid;

3.使用Snowflake生成雪花id

(1)建立一个文件保存代码方便使用(若直接放入代码中使用,容易混乱)

(2)引入并使用(这里我将机器码workerid设置为1)

const GenId = require("../utils/SnowFlake")//引入文件

const genid = new GenId({WorkerId: 1})//创建实例
let id = genid.NextId() //使用使用生成标识
console.log(id)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值