生成一個隨機的 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來做區分)