JS 雪花算法生成隨機 ID

生成一個隨機的 ID 有很多種做法,比如說 GUID 和 UUID。但如果想要有序,可以插入數據庫中做數字主鍵,那就有了雪花算法。雪花算法得到的是個比較大的數字,比較大,而 JS 中 Number 類型的最大值 Number.MAX_SAFE_INTEGER:9007199254740991,那這樣運算會溢出。所幸的是網上有很多 BigInt 的類庫,現在 ES10 標準就包括了它,並且 Chrome 67 也實現了支持。

原理

下圖出自理解分佈式id生成算法

SnowFlake算法生成id的結果是一個64bit大小的整數,它的結構如下圖:

代碼

var Snowflake = (function() {
    function Snowflake(_workerId, _dataCenterId, _sequence) {
        this.twepoch = 1288834974657n;
        //this.twepoch = 0n;
        this.workerIdBits = 5n;
        this.dataCenterIdBits = 5n;
        this.maxWrokerId = -1n ^ (-1n << this.workerIdBits); // 值爲:31
        this.maxDataCenterId = -1n ^ (-1n << this.dataCenterIdBits); // 值爲:31
        this.sequenceBits = 12n;
        this.workerIdShift = this.sequenceBits; // 值爲:12
        this.dataCenterIdShift = this.sequenceBits + this.workerIdBits; // 值爲:17
        this.timestampLeftShift = this.sequenceBits + this.workerIdBits + this.dataCenterIdBits; // 值爲:22
        this.sequenceMask = -1n ^ (-1n << this.sequenceBits); // 值爲:4095
        this.lastTimestamp = -1n;
        //設置默認值,從環境變量取
        this.workerId = 1n;
        this.dataCenterId = 1n;
        this.sequence = 0n;
        if (this.workerId > this.maxWrokerId || this.workerId < 0) {
            throw new Error('_workerId must max than 0 and small than maxWrokerId-[' + this.maxWrokerId + ']');
        }
        if (this.dataCenterId > this.maxDataCenterId || this.dataCenterId < 0) {
            throw new Error('_dataCenterId must max than 0 and small than maxDataCenterId-[' + this.maxDataCenterId + ']');
        }

        this.workerId = BigInt(_workerId);
        this.dataCenterId = BigInt(_dataCenterId);
        this.sequence = BigInt(_sequence);
    }
    Snowflake.prototype.tilNextMillis = function(lastTimestamp) {
        var timestamp = this.timeGen();
        while (timestamp <= lastTimestamp) {
            timestamp = this.timeGen();
        }
        return BigInt(timestamp);
    };
    Snowflake.prototype.timeGen = function() {
        return BigInt(Date.now());
    };
    Snowflake.prototype.nextId = function() {
        var timestamp = this.timeGen();
        if (timestamp < this.lastTimestamp) {
            throw new Error('Clock moved backwards. Refusing to generate id for ' +
                (this.lastTimestamp - timestamp));
        }
        if (this.lastTimestamp === timestamp) {
            this.sequence = (this.sequence + 1n) & this.sequenceMask;
            if (this.sequence === 0n) {
                timestamp = this.tilNextMillis(this.lastTimestamp);
            }
        } else {
            this.sequence = 0n;
        }
        this.lastTimestamp = timestamp;
        return ((timestamp - this.twepoch) << this.timestampLeftShift) |
            (this.dataCenterId << this.dataCenterIdShift) |
            (this.workerId << this.workerIdShift) |
            this.sequence;
    };
    return Snowflake;
}());

注意代碼中的 this.twepoch = 1288834974657n;。如果 ((timestamp - 1288834974657) << 32) 超過long類型數據範圍,怎麼辦?可以保證69年不超,1288834974657這個初始值是可以設置的。

使用如下

console.time();
var tempSnowflake = new Snowflake(1n, 1n, 0n);
var tempIds = [];
for (var i = 0; i < 10000; i++) {
    var tempId = tempSnowflake.nextId();
    console.log(tempId);
    if (tempIds.indexOf(tempId) < 0) {
        tempIds.push(tempId);
    }
}
console.log(tempIds.length);
console.timeEnd();

附錄-GUID/UUID

原文:https://www.cnblogs.com/snandy/p/3261754.html

全局唯一標識符(GUID,Globally Unique Identifier)也稱作 UUID(Universally Unique IDentifier) 。

GUID是一種由算法生成的二進制長度爲128位的數字標識符。GUID 的格式爲“xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx”,其中的 x 是 0-9 或 a-f 範圍內的一個32位十六進制數。在理想情況下,任何計算機和計算機集羣都不會生成兩個相同的GUID。

GUID 的總數達到了2128(3.4×1038)個,所以隨機生成兩個相同GUID的可能性非常小,但並不爲0。GUID一詞有時也專指微軟對UUID標準的實現。

function guid() {
    return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
        var r = Math.random() * 16 | 0, v = c == 'x' ? r : (r&0x3 | 0x8);
        return v.toString(16);
    });
}

由於在Java中64bit的整數是long類型,所以在Java中SnowFlake算法生成的id就是long來存儲的。

SnowFlake可以保證:

  • 所有生成的id按時間趨勢遞增
  • 整個分佈式系統內不會產生重複id(因爲有datacenterId和workerId來做區分)
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章