Twitter-Snowflake 64位自增ID簡介

      在系統中,咱們須要爲每一個資源設置一個惟一ID,單表時代,使用數據庫的自增ID能夠很簡單的達到咱們的目的,可是在分佈式系統、多庫多表的狀況下,數據庫自增ID就不靈了,所以,咱們須要另外一種算法來實現分佈式環境下的惟一ID。html

      Snowflake核心算法:java

     

      把時間戳、工做id和序列號拼裝起來,生成一個64bit的惟一id,符號位不用,其餘三組bit能夠根據業務須要來進行調整,默認方案下,能使用69年,部署1023臺機器,每毫秒能產生4095個自增ID。python

      這裏的69年,1023臺機器,4095個自增ID是怎麼算出來的的?git

      假設41個bit都是1,那麼轉換成10進制就是 2**41-1 = 2199023255552毫秒≈69年,機器數量、自增ID數量也是如此計算。github

      生成惟一ID的方式不少,爲何是Snowflake?算法

      一、相對有序,數據庫插入性能好。數據庫

      二、Long 型存儲,相對於guid uuid佔用空間小。less

 

JAVA 實現:分佈式

public final class Snowflake {

    private static final Long workerIdBits = 10L;
    private static final Long maxWorkerId = -1L ^ (-1L << workerIdBits);
    private static final Long sequenceBits = 12L;
    private static final Long maxSequence = -1L ^ (-1L << sequenceBits);

    private static final Long workerIdShift = sequenceBits;
    private static final Long timestampLeftShift = sequenceBits + workerIdBits;

    private final SimpleDateFormat format = new SimpleDateFormat("yyyyMMddHHmmssSSS");

    private final Long workerId;

    private Long sequence = 0L;
    private Long lastTimestamp = -1L;

    private Long since = 0l;

    public Snowflake(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;

        Calendar calendar = Calendar.getInstance();
        calendar.set(2017, Calendar.NOVEMBER, 3, 0, 0, 0);

        this.since = calendar.getTimeInMillis();
    }

    public synchronized Long nextId() {
        long timestamp = this.timeGen();

        if (this.lastTimestamp == timestamp) {
            this.sequence = (this.sequence + 1) & this.maxSequence;

            if (this.sequence == 0) {
                timestamp = this.tilNextMillis(this.lastTimestamp);
            }
        } else {
            this.sequence = 0L;
        }

        if (timestamp < this.lastTimestamp) {
            throw new RuntimeException(
                    String.format(
                            "Clock moved backwards.  Refusing to generate id for %d milliseconds",
                            this.lastTimestamp - timestamp));
        }

        this.lastTimestamp = timestamp;

        return ((timestamp - since) << timestampLeftShift) | (this.workerId << this.workerIdShift) | (this.sequence);
    }


    /**
     * 生成
     * prefix 長度最多3個字符
     * 返回的字符串長度最多26個字符
     *
     * @param prefix
     * @return
     */
    public synchronized String nextSerialNumber(String prefix) {
        prefix = StringUtils.trimToEmpty(prefix);

        if (prefix.length() > 3) throw new BusinessException("prefix長度不能大於3", Error.ILLEGAL_ARGUMENT);

        long timestamp = this.timeGen();

        if (this.lastTimestamp == timestamp) {
            this.sequence = (this.sequence + 1) & this.maxSequence;

            if (this.sequence == 0) {
                timestamp = this.tilNextMillis(this.lastTimestamp);
            }
        } else {
            this.sequence = 0L;
        }

        if (timestamp < this.lastTimestamp) {
            throw new RuntimeException(
                    String.format(
                            "Clock moved backwards.  Refusing to generate id for %d milliseconds",
                            this.lastTimestamp - timestamp));
        }

        this.lastTimestamp = timestamp;

        String time = format.format(new Date());

        return String.format("%s%s%s%6d", prefix, time, workerId, this.sequence);
    }

    private long tilNextMillis(final long lastTimestamp) {
        long timestamp = this.timeGen();

        while (timestamp <= lastTimestamp) {
            timestamp = this.timeGen();
        }

        return timestamp;
    }

    private long timeGen() {
        return System.currentTimeMillis();
    }
}

 

py3實現性能

# -*- coding: utf-8 -*-
import time

WORKER_ID_BITS = 10
MAX_WORK_ID = -1^(-1<<WORKER_ID_BITS)
SEQUENCE_BITS = 12
MAX_SEQUENCE = -1^(-1<<SEQUENCE_BITS)

TIMESTAMP_LEFT_SHIFT = SEQUENCE_BITS+WORKER_ID_BITS
WORKER_ID_SHIFT = SEQUENCE_BITS

class Snowflake(object):
    def __init__(self,workerId):
        self.workerId = workerId
        self.sequence = 0
        self.lastTimestamp = -1
        self.since = 0

    def nextId(self):
        timestamp = self._timestamp()
        if self.lastTimestamp == timestamp:
            self.sequence = (self.sequence + 1) & MAX_SEQUENCE
            if self.sequence == 0:
                timestamp = self._tilNextMillis()
        else:
            self.sequence = 0

        self.lastTimestamp = timestamp

        return (timestamp - self.since) << TIMESTAMP_LEFT_SHIFT | self.workerId << WORKER_ID_SHIFT | self.sequence

    def _timestamp(self):
        return int(round(time.time()*1000))

    def _tilNextMillis(self):
        timestamp=self._timestamp()
        while timestamp<self.lastTimestamp:
            timestamp = self._timestamp()

        return timestamp

 

參考:

1. https://github.com/twitter/snowflake

2. http://www.cnblogs.com/relucent/p/4955340.html

3.http://www.lanindex.com/twitter-snowflake%EF%BC%8C64%E4%BD%8D%E8%87%AA%E5%A2%9Eid%E7%AE%97%E6%B3%95%E8%AF%A6%E8%A7%A3/

相關文章
相關標籤/搜索