Js随机ID

转自:https://www.cnblogs.com/everlose/p/12855745.html

https://www.cnblogs.com/du-blog/p/9250660.html

生成一个随机的 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来做区分)

Snowflake(雪花算法)的JavaScript实现

  现在好多的ID都是服务器端生成的,当然JS也可以生成GUID或者UUID之类的,但是如果想要有序……这时就想到了雪花算法,但是都知道JS中Number的最大值为Number.MAX_SAFE_INTEGER:9007199254740991。在雪花算法中,有的操作在JS中会溢出。不过还好,网上有好多BigInt的类库,例如本例使用的:http://peterolson.github.io/BigInteger.js/ ,还有就是chrome67 原生支持BigInt类型,这是个好消息……

  参考文章: 理解分布式id生成算法SnowFlake

  类库:http://peterolson.github.io/BigInteger.js/

  CDN:https://cdnjs.com/

  记录一下代码

  类库方式实现:

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>

<body>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/big-integer/1.6.32/BigInteger.min.js"></script>
    <!-- <script src="http://peterolson.github.com/BigInteger.js/BigInteger.min.js"></script> -->
    <script>
        var Snowflake = /** @class */ (function() {
            function Snowflake(_workerId, _dataCenterId, _sequence) {
                // this.twepoch = 1288834974657;
                this.twepoch = 0;
                this.workerIdBits = 5;
                this.dataCenterIdBits = 5;
                this.maxWrokerId = -1 ^ (-1 << this.workerIdBits); // 值为:31
                this.maxDataCenterId = -1 ^ (-1 << this.dataCenterIdBits); // 值为:31
                this.sequenceBits = 12;
                this.workerIdShift = this.sequenceBits; // 值为:12
                this.dataCenterIdShift = this.sequenceBits + this.workerIdBits; // 值为:17
                this.timestampLeftShift = this.sequenceBits + this.workerIdBits + this.dataCenterIdBits; // 值为:22
                this.sequenceMask = -1 ^ (-1 << this.sequenceBits); // 值为:4095
                this.lastTimestamp = -1;
                //设置默认值,从环境变量取
                this.workerId = 1;
                this.dataCenterId = 1;
                this.sequence = 0;
                if (this.workerId > this.maxWrokerId || this.workerId < 0) {
                    throw new Error('config.worker_id must max than 0 and small than maxWrokerId-[' + this.maxWrokerId + ']');
                }
                if (this.dataCenterId > this.maxDataCenterId || this.dataCenterId < 0) {
                    throw new Error('config.data_center_id must max than 0 and small than maxDataCenterId-[' + this.maxDataCenterId + ']');
                }
                this.workerId = _workerId;
                this.dataCenterId = _dataCenterId;
                this.sequence = _sequence;
            }
            Snowflake.prototype.tilNextMillis = function(lastTimestamp) {
                var timestamp = this.timeGen();
                while (timestamp <= lastTimestamp) {
                    timestamp = this.timeGen();
                }
                return timestamp;
            };
            Snowflake.prototype.timeGen = function() {
                //new Date().getTime() === Date.now()
                return 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 + 1) & this.sequenceMask;
                    if (this.sequence === 0) {
                        timestamp = this.tilNextMillis(this.lastTimestamp);
                    }
                } else {
                    this.sequence = 0;
                }
                this.lastTimestamp = timestamp;
                var shiftNum = (this.dataCenterId << this.dataCenterIdShift) |
                    (this.workerId << this.workerIdShift) |
                    this.sequence; // dataCenterId:1,workerId:1,sequence:0  shiftNum:135168
                var nfirst = new bigInt(String(timestamp - this.twepoch), 10);
                nfirst = nfirst.shiftLeft(this.timestampLeftShift);
                var nnextId = nfirst.or(new bigInt(String(shiftNum), 10)).toString(10);
                return nnextId;
            };
            return Snowflake;
        }());
        var tempSnowflake = new Snowflake(1, 1, 0);
        var tempIds = [];
        console.time();
        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();
    </script>
</body>

</html>

原生BigInt实现:

 

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>

<body>
    <script>
        var Snowflake = /** @class */ (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;
        }());
        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();
    </script>
</body>

</html>

 

 

 

posted @ 2021-09-11 10:10  凉茶微凉  阅读(875)  评论(0)    收藏  举报