Twitter's distributed self-increasing ID algorithm snowflake (snowflake algorithm) C# and Java Edition

Overview


In distributed systems, there are some scenarios that require the use of a globally unique ID. In this case, a 36-bit UUID can be used to prevent ID conflicts.
However, UUID has some disadvantages. First, it is relatively long, and UUIDs are generally disordered.
Sometimes we hope to use a simpler ID, and hope that the ID can be generated in an orderly manner.
Twitter's snowflake solves this demand. Initially, Twitter migrated the storage system from MySQL to Cassandra.
Because Cassandra did not have a sequential ID generation mechanism, it developed such a global unique ID generation service.


structure

The structure of snowflake is as follows (each part is separated by -):
0-0000000000 0000000000 0000000000 0000000000 0-00000-00000-000000000000 The
first digit is unused, and the next 41 digits are millisecond time (41 digits can be used for 69 years ), and then 5 datacenterId 5
workerId (10 bits up to the length of the deployment of support nodes 1024), the last 12 bits are the count (count of sequence number 12 of each support section in the millisecond
point ID 4096 is generated every millisecond The serial number)
adds up to exactly 64 bits, which is a Long type. (The length is up to 19 after being converted into a character string)
The IDs generated by snowflake are sorted by time increment as a whole, and there will be no ID collisions in the entire distributed system (differentiated by datacenter and
workerId), and the efficiency is high. Snowflake can generate 260,000 IDs per second after testing

C# 

public class IdWorker
    {
        //机器ID
        private static long workerId;
        private static long twepoch = 687888001020L; //唯一时间,这是一个避免重复的随机量,自行设定不要大于当前时间戳
        private static long sequence = 0L;
        private static int workerIdBits = 4; //机器码字节数。4个字节用来保存机器码(定义为Long类型会出现,最大偏移64位,所以左移64位没有意义)
        public static long maxWorkerId = -1L ^ -1L << workerIdBits; //最大机器ID
        private static int sequenceBits = 10; //计数器字节数,10个字节用来保存计数码
        private static int workerIdShift = sequenceBits; //机器码数据左移位数,就是后面计数器占用的位数
        private static int timestampLeftShift = sequenceBits + workerIdBits; //时间戳左移动位数就是机器码和计数器总字节数
        public static long sequenceMask = -1L ^ -1L << sequenceBits; //一微秒内可以产生计数,如果达到该值则等到下一微妙在进行生成
        private long lastTimestamp = -1L;

        /// <summary>
        /// 机器码
        /// </summary>
        /// <param name="workerId"></param>
        public IdWorker(long workerId)
        {
            if (workerId > maxWorkerId || workerId < 0)
                throw new Exception(string.Format("worker Id can't be greater than {0} or less than 0 ", workerId));
            IdWorker.workerId = workerId;
        }

        public long nextId()
        {
            lock (this)
            {
                long timestamp = timeGen();
                if (this.lastTimestamp == timestamp)
                { //同一微妙中生成ID
                    IdWorker.sequence = (IdWorker.sequence + 1) & IdWorker.sequenceMask; //用&运算计算该微秒内产生的计数是否已经到达上限
                    if (IdWorker.sequence == 0)
                    {
                        //一微妙内产生的ID计数已达上限,等待下一微妙
                        timestamp = tillNextMillis(this.lastTimestamp);
                    }
                }
                else
                { //不同微秒生成ID
                    IdWorker.sequence = 0; //计数清0
                }
                if (timestamp < lastTimestamp)
                { //如果当前时间戳比上一次生成ID时时间戳还小,抛出异常,因为不能保证现在生成的ID之前没有生成过
                    throw new Exception(string.Format("Clock moved backwards.  Refusing to generate id for {0} milliseconds",
                        this.lastTimestamp - timestamp));
                }
                this.lastTimestamp = timestamp; //把当前时间戳保存为最后生成ID的时间戳
                long nextId = (timestamp - twepoch << timestampLeftShift) | IdWorker.workerId << IdWorker.workerIdShift | IdWorker.sequence;
                return nextId;
            }
        }

        /// <summary>
        /// 获取下一微秒时间戳
        /// </summary>
        /// <param name="lastTimestamp"></param>
        /// <returns></returns>
        private long tillNextMillis(long lastTimestamp)
        {
            long timestamp = timeGen();
            while (timestamp <= lastTimestamp)
            {
                timestamp = timeGen();
            }
            return timestamp;
        }

        /// <summary>
        /// 生成当前时间戳
        /// </summary>
        /// <returns></returns>
        private long timeGen()
        {
            return (long)(DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc)).TotalMilliseconds;
        }
    }

C# call

      IdWorker idworker = new IdWorker(1);
      for (int i = 0; i < 1000; i++)
      {
           Response.Write(idworker.nextId() + "<br/>");
      }

 java

public class IdWorker {
    private final long workerId;
    private final static long twepoch = 1288834974657L;
    private long sequence = 0L;
    private final static long workerIdBits = 4L;
    public final static long maxWorkerId = -1L ^ -1L << workerIdBits;
    private final static long sequenceBits = 10L;
    private final static long workerIdShift = sequenceBits;
    private final static long timestampLeftShift = sequenceBits + workerIdBits;
    public final static long sequenceMask = -1L ^ -1L << sequenceBits;
    private long lastTimestamp = -1L;
    public IdWorker(final long workerId) {
        super();
        if (workerId > this.maxWorkerId || workerId < 0) {
              throw new IllegalArgumentException(String.format(
                                          "worker Id can't be greater than %d or less than 0",
                    this.maxWorkerId));
        }
        this.workerId = workerId;
    }
    public synchronized long nextId() {
        long timestamp = this.timeGen();
        if (this.lastTimestamp == timestamp) {
            this.sequence = (this.sequence + 1) & this.sequenceMask;
            if (this.sequence == 0) {
                System.out.println("###########" + sequenceMask);
                timestamp = this.tilNextMillis(this.lastTimestamp);
             }
            } else {
                this.sequence = 0;
            }
            if (timestamp < this.lastTimestamp) {
                try {
                    throw new Exception(
                          String.format(
                              "Clock moved backwards. Refusing to generate id for %d milliseconds",
                              this.lastTimestamp - timestamp));
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
 
        this.lastTimestamp = timestamp;
        long nextId = ((timestamp - twepoch << timestampLeftShift))
              | (this.workerId << this.workerIdShift) | (this.sequence);
        System.out.println("timestamp:" + timestamp + ",timestampLeftShift:"
                                      + timestampLeftShift + ",nextId:" + nextId + ",workerId:"
                                      + workerId + ",sequence:" + sequence);
        return nextId;
    }
 
    private long tilNextMillis(final long lastTimestamp) {
        long timestamp = this.timeGen();
        while (timestamp <= lastTimestamp) {
            timestamp = this.timeGen();
        }
        return timestamp;
    }
 
    private long timeGen() {
        return System.currentTimeMillis();
    }
 
    //调用
    public static void main(String[] args){
        IdWorker worker2 = new IdWorker(2);
        System.out.println(worker2.nextId());
    }
}

Reprinted from: https://www.jianshu.com/p/521dde97d3aa

Guess you like

Origin blog.csdn.net/lw112190/article/details/107106006