ID generation algorithm (a) - Snow Algorithm

JavaScript and orderly generate GUID or UUID, then I think of the snow algorithm.

 

Introduce the principle:

Results snowFlake algorithm ID is finally generated a 64bit integer size, the structure as shown below:

Explanation:

  • 1bit. Binary 1 indicates the highest bit negative, but we finally generated ID is generally integers, so the highest bit is fixed to 0.
  • 41bit. For recording a timestamp (in milliseconds)
    • 41bit can represents 41 is -1 figures
    • If only used to represent a positive integer (including 0 Number of Computer CKS), the value range is 0 to represent 2 41 is -1, 1 is reduced as the value range representable count from zero, rather than 1.
    • I.e. 41bit can represents 41 is -1 millisecond to convert the value of (2 41 is  - 1) / (60 * 1000 * 365 * 60 * 24) = 6973 years.
  • 10bit. For recording machine ID
    • It may be used for the deployment of 10 = 1024 nodes, comprising the datacenterId 5bit and 5bit of workerId
    • 5bit maximum positive integer that can be represented is 2 . 5 - 1 = 31 i.e., it may be 0,1,2,3 .... 31 represent 32 different numbers and workerId datacenterId
  • 12bit. ID numbers for generating different sequences of the same record ms
    • 12bit Maximum positive integer can be represented by 2 12 is -1 = 4095, 4094 ... which can be 0,1,2,3 numerals 4095 generated in the same machine in the same timestamp (in milliseconds) ID number 4095

snowFlake algorithm can ensure that: all the generated ID is incremented by the time trend; ID no repeating throughout the distributed system, since the datacenterId and 5bit workerId 5bit be distinguished. 

 

Algorithm code implements the principle of interpretation:

Computer negative binary complement is expressed.

Is assumed to store digital type int, int type 32bit bit size is four byte. (1byte = 8bit)

Then the decimal representation in binary 3 should be:

00000000 00000000 0,000,000,000,000,011 // 3 of the original binary code  

So the number -3 in the binary representation of what should be? Imagine: -3 + 3 = 0 in the binary arithmetic binary -3 X solved as unknowns.

    00000000   00000000   00000000   00000011   // 3 原码
+   xxxxxxxx   xxxxxxxx   xxxxxxxx   xxxxxxxx   //  -3 补码
------------------------------------------------------
    00000000   00000000   00000000   00000000

Backstepping i.e. binary number X from the lowest-order bit by bit plus 1, so that an overflow of the overflow constantly high, until it overflows into the first 33, then since the int type can hold up to 32 bits, so the maximum overflow bit 1 The remaining 32 became 0.

then:

    00000000 00000000 0,000,000,000,000,011    @ 3 primitive 
+ 1,111,111,111,111,111 1,111,111,111,111,101    @   -3 complement 
------------------------------ --------------------------- 
  100000000 00000000 00000000

Summary formula:

  • Anti-complement code = 1 +
  • Complement = (Original Code - 1) and then take the inverted

workerIdBits = 5L;
maxWorkerId = -1L ^ (-1L << workerIdBits);

After 5 -1 left upper overflow discarded to give a, a XOR operation with -1 to get the final result.

               1,111,111,111,111,111 1,111,111,111,111,111    @ -1 complement 
       1111111111111 11111111 1,111,111,111,100,000    
----------------------------------- ---------------------------------- 
               1,111,111,111,111,111 1,111,111,111,100,000    //   high overflow discarded  
               1,111,111,111,111,111 1,111,111,111,111,111    @ -1 complement 
    ^ 1,111,111,111,111,111 1,111,111,111,100,000    
----------------------------------- ---------------------------------- 00000000 00000000 0,000,000,000,011,111
               

24+23+22+21+2= 16+8+4+2+1 = 31

-1L ^ (-1L << 5L) = 31 which is 2 5 -1 = 31, which is written using a bit operation to calculate the maximum positive integer of 5 potential energy is.

 

Mask with a mask to prevent overflow

seq = (seq  + 1) & seqMask 

This code results range by a bitwise AND operation is always guaranteed computing 0--4095. 

 

Bitwise results:

return   ((timestamp - twepoch) << timestampLeftShift) |
            (datacnterId << datacenterIdShift) |
            (workerId << workerIdShift) |
            sequence;

解析:

var twepoch = 1571192786565; // 起始时间戳 用于当前时间戳减去这个时间戳得到偏移量
var workerIdBits = 5; // workId占用的位数5
var datacenterIdBit = 5;// datacenterId占用的位数5
var maxWorkerId = -1 ^ (-1 << workerIdBits); // workId可以使用的最大数值31
var maxDatacenterId = -1 ^ (-1 << datacenterIdBits); // datacenterId可以使用的最大数值31
var sequenceBit = 12;// 序列号占用的位数12
workerIdShift = sequenceBits; // 12 
datacenterIdShift = sequenceBits + workerIdBits; // 12+5 = 17
timestampLeftShift = sequenceBits + workerIdBits + datacenterIdBits; // 12+5+5 = 22
sequenceMask = -1 ^ (-1 << sequenceBits); // 4095
lastTimestamp = -1;

  

JavaScript中Number的最大值为Number.MAX_SAFE_INTEGER:9007199254740991。在雪花算法中,有的操作在JS中会溢出,所以选用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>Snowflake</title> 
</head> 
<body> 
<script>
  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;
        }());
        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>

 

  

  

Guess you like

Origin www.cnblogs.com/sunyuweb/p/11673001.html