`

Twitter的分布式自增ID算法Snowflake

 
阅读更多

Twitter-Snowflake算法产生的背景相当简单,为了满足Twitter每秒上万条消息的请求,每条消息都必须分配一条唯一的id,这些id还需要一些大致的顺序(方便客户端排序),并且在分布式系统中不同机器产生的id必须不同。

Snowflake算法核心

时间戳工作机器id序列号组合在一起。

 

snowflake-64bit

 

除了最高位bit标记为不可用以外,其余三组bit占位均可浮动,看具体的业务需求而定。默认情况下41bit的时间戳可以支持该算法使用到2082年,10bit的工作机器id可以支持1023台机器,序列号支持1毫秒产生4095个自增序列id。下文会具体分析。

 

Snowflake – 时间戳

这里时间戳的细度是毫秒级,具体代码如下,建议使用64位linux系统机器,因为有vdso,gettimeofday()在用户态就可以完成操作,减少了进入内核态的损耗。

1
2
3
4
5
6
uint64_t generateStamp()
{
    timeval tv;
    gettimeofday(&tv, 0);
    return (uint64_t)tv.tv_sec * 1000 + (uint64_t)tv.tv_usec / 1000;
}

默认情况下有41个bit可以供使用,那么一共有T(1llu << 41)毫秒供你使用分配,年份 = T / (3600 * 24 * 365 * 1000) = 69.7年。如果你只给时间戳分配39个bit使用,那么根据同样的算法最后年份 = 17.4年。

Snowflake – 工作机器id

严格意义上来说这个bit段的使用可以是进程级,机器级的话你可以使用MAC地址来唯一标示工作机器工作进程级可以使用IP+Path来区分工作进程。如果工作机器比较少,可以使用配置文件来设置这个id是一个不错的选择,如果机器过多配置文件的维护是一个灾难性的事情。

这里的解决方案是需要一个工作id分配的进程,可以使用自己编写一个简单进程来记录分配id,或者利用Mysql auto_increment机制也可以达到效果。

snowflake - 工作id

 

工作进程与工作id分配器只是在工作进程启动的时候交互一次,然后工作进程可以自行将分配的id数据落文件,下一次启动直接读取文件里的id使用。

PS:这个工作机器id的bit段也可以进一步拆分,比如用前5个bit标记进程id,后5个bit标记线程id之类:D

Snowflake – 序列号

序列号就是一系列的自增id(多线程建议使用atomic),为了处理在同一毫秒内需要给多条消息分配id,若同一毫秒把序列号用完了,则“等待至下一毫秒”。

1
2
3
4
5
6
7
8
uint64_t waitNextMs(uint64_t lastStamp)
{
    uint64_t cur = 0;
    do {
        cur = generateStamp();
    } while (cur <= lastStamp);
    return cur;
}

 

总体来说,是一个很高效很方便的GUID产生算法,一个int64_t字段就可以胜任,不像现在主流128bit的GUID算法,即使无法保证严格的id序列性,但是对于特定的业务,比如用做游戏服务器端的GUID产生会很方便。另外,在多线程的环境下,序列号使用atomic可以在代码实现上有效减少锁的密度。

 

结构为:

0---0000000000 0000000000 0000000000 0000000000 0 --- 00000 ---00000 ---0000000000 00

在上面的字符串中,第一位为未使用(实际上也可作为long的符号位),接下来的41位为毫秒级时间,然后5位datacenter标识位,5位机器ID(并不算标识符,实际是为线程标识),然后12位该毫秒内的当前毫秒内的计数,加起来刚好64位,为一个Long型。

这样的好处是,整体上按照时间自增排序,并且整个分布式系统内不会产生ID碰撞(由datacenter和机器ID作区分),并且效率较高,经测试,snowflake每秒能够产生26万ID左右,完全满足需要。

 

public class IdWorker {
    private final long        workerId;
    private final static long twepoch            = 1361753741828L;
    private long              sequence           = 0L;
    private final static long workerIdBits       = 4L;
    private 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;
    private final static long sequenceMask       = -1L ^ -1L << sequenceBits;

    private long              lastTimestamp      = -1L;

    public IdWorker(final long workerId) {
        super();
        if (workerId > maxWorkerId || workerId < 0) {
            throw new IllegalArgumentException(String.format(
                "worker Id can't be greater than %d or less than 0", maxWorkerId));
        }
        this.workerId = workerId;
    }

    public synchronized long nextId() {
        long timestamp = this.timeGen();
        if (this.lastTimestamp == timestamp) {
            this.sequence = (this.sequence + 1) & 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 << 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) throws InterruptedException {
        IdWorker worker2 = new IdWorker(2);
        System.out.println(worker2.nextId());
        Thread.sleep(1000L);
        System.out.println(worker2.nextId());
    }
}

 

 

Reference:

http://www.lanindex.com/twitter-snowflake/

分享到:
评论

相关推荐

    Twitter的分布式自增ID雪花算法snowflake

    综上所述,Twitter的雪花算法(Snowflake)提供了一种高效、简单的分布式ID生成方案,通过合理的结构设计,确保了全局唯一性和顺序性。在Java环境下,可以通过实现相应的类和方法轻松地引入这一机制,为分布式系统的...

    Java实现Twitter的分布式自增ID算法snowflake

    【Java实现Twitter的分布式自增ID算法snowflake】 在分布式系统设计中,生成全局唯一ID是一个常见的需求。Twitter的Snowflake算法就是为了解决这个问题而诞生的,它提供了一种高效、有序且不会冲突的ID生成策略。...

    Twitter的分布式自增ID算法Snowflake的PHP实现,Snowflake PHP版本,高并发唯一id,全局唯一id,不重复id

    Twitter Snowflake算法,php版代码; 请见博客: http://blog.csdn.net/envon123/article/details/52953872

    snowflake-C语言实现分布式自增有序的唯一ID生成算法

    We have retired the initial release of Snowflake and working on open sourcing the next version based on Twitter-server, in a form that can run anywhere without requiring Twitter's own infrastructure ...

    springboot分布式自增id_javaredis_源码

    在分布式系统中,为了生成全局唯一的自增ID,可以使用Snowflake算法、Twitter的分布式ID生成服务,或者基于Redis的序列号生成方案。Spring Boot可以轻松集成这些方案,通过配置和注解简化开发流程。 2. **Redis作为...

    SnowFlake:Twitter的分布式自增ID雪花算法snowflake(Java版)

    而twitter的snowflake解决了这种需求,最初是Twitter把存储系统从MySQL迁移到Cassandra,因为Cassandra没有顺序的ID生成机制,所以开发了这样一套唯一的ID生成服务。结构雪花的结构如下(每部分用-分开): 0 - ...

    分布式ID生成策略_snowflake算法

    Snowflake算法是由Twitter开源的一种高效且可扩展的分布式ID生成方案,广泛应用于Java和其他编程语言的系统中。 Snowflake算法的核心思想是将64位的整数划分为不同的部分,分别为: 1. **时间戳**(41位):自定义...

    java快速ID自增器

    3. **分布式ID生成器**:如Snowflake算法,它是由Twitter开源的一种分布式ID生成方案。通过时间戳、工作机器ID和序列号三部分组合,可以生成全局唯一的ID,而且排序性好。在Java中,可以使用诸如Snowflake或者其变种...

    java 获取分布式唯一ID.雪花ID

    public class SnowflakeIdGenerator { private static final SnowflakeIdWorker idWorker = new SnowflakeIdWorker(0, 0); public static long generateId() { return idWorker.nextId(); } } ``` 在这个例子中...

    分布式id公开课.pptx

    - 雪花算法由Twitter提出,生成的ID由时间戳、工作节点ID和序列号三部分组成,全局唯一且有序。 - 时间戳确保ID的全局唯一性和时间顺序;工作节点ID确保不同节点生成的ID不会冲突;序列号在每个节点内部确保唯一性...

    分布式数据库唯一主键设计

    为解决此问题,可以使用全局自增序列,如Twitter的Snowflake算法,它结合时间戳、工作节点ID和序列号生成全局唯一的64位ID。 2. UUID(通用唯一标识符):UUID是一种广泛使用的全局唯一ID生成方案,但其128位长度...

    应用级自增ID的生成

    1. **Snowflake算法**:这是一种常见的分布式ID生成策略,由Twitter开源。它通过时间戳、工作节点ID和序列号三部分组成,确保全局唯一性。然而,这里的描述并没有明确提及Snowflake,而是暗示了可能有其他自定义实现...

    Twitter Snowflake C#版源码

    Twitter的Snowflake是一种分布式ID生成算法,用于在大规模分布式系统中生成全局唯一的、有序的64位整数ID。这个算法由Twitter开源,解决了在分布式环境下如何生成具有时间序列属性且不重复的ID的问题。现在,我们将...

    Python-分布式系统中常用的的算法python实现

    12. **分布式ID生成器**: 如Twitter的Snowflake算法,生成全局唯一且递增的ID。Python实现将关注时间戳、机器标识和序列号的组合。 项目中的实用工具类可能包括哈希函数、网络通信模块、并发控制、日志记录、配置...

    snowflake-net-master_snowflake_算法_

    雪花算法是由Twitter开源的一种分布式ID生成算法,它能够为分布式系统中的每个实体生成全局唯一的、单调递增的64位整数ID。这种算法在大数据和分布式环境下广泛应用于主键生成,因为它解决了在分布式环境下的ID唯一...

    分布式ID生成策略(1)_snowflake算法

    Snowflake算法就是一种被广泛使用的分布式ID生成方案,它由Twitter开源,具有时间戳、工作机器ID和序列号三部分组成,能够确保在分布式环境下生成的ID具有唯一性、有序性和高性能。 Snowflake算法的核心思想是将64...

    Go-GolangMysql实现的分布式ID生成服务

    Twitter开源的Snowflake算法是一种常用的分布式ID生成策略,它将ID分为三部分:时间戳(41位)、工作机器ID(10位)和序列号(12位)。通过这种方式,可以保证ID的全局唯一性,并且有序。 #### 3.2 UUID UUID...

    细聊分布式ID生成方法.pdf

    这是一种基于Twitter开源的ID生成算法。它通过将ID分成多个部分(如时间戳、机器ID、序列号等),从而保证了ID的全局唯一性。此算法广泛应用于高并发场景下的分布式系统中。 - **优点**:支持高并发,性能优越;...

    cpp-idgen是一个可以生成全局唯一自增id的分布式的高可用服务

    cpp-idgen可能采用了一种类似于Twitter的Snowflake算法或者百度的WID(Worker ID)算法,这些算法将ID分为多个部分,如时间戳、工作节点ID和序列号,确保了ID的唯一性和顺序性。具体实现可能会包括以下几个步骤: - ...

Global site tag (gtag.js) - Google Analytics