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
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Twitter 的 Snowflake 算法是一种生成唯一 ID 的算法,它可以生成更加复杂的 ID,以避免重复。Snowflake 算法核心思想是,使用一个 64 位的整数作为 ID,其中,高 41 位表示时间戳,中间 10 位表示机器 ID,低 13 位表示序列号。这样,就可以在多台机器上生成不同的 ID,而且在同一台机器上也可以生成不同的 ID。 具体来说,Snowflake 算法的实现主要包括三个部分: 1. 时间戳:使用当前时间戳减去一个固定的起始时间戳,得到一个相对时间戳。由于使用的是相对时间戳,所以即使系统时间被修改,也不会影响生成的 ID。 2. 机器 ID:使用一个固定的机器 ID,可以是 IP 地址或者其他标识符,来标识不同的机器。由于使用的是 10 位二进制数,所以最多可以标识 1024 台机器。 3. 序列号:由于在同一毫秒内,可能会生成多个 ID,因此需要使用一个序列号来标识不同的 ID。由于使用的是 13 位二进制数,所以最多可以生成 4096 个不同的 ID。 使用 Snowflake 算法生成 ID 的代码如下: ```java public class SnowflakeIdGenerator { private static final long START_TIMESTAMP = 1480166465631L; private static final long MACHINE_ID_BITS = 10L; private static final long SEQUENCE_BITS = 13L; private static final long MAX_MACHINE_ID = -1L ^ (-1L << MACHINE_ID_BITS); private static final long MAX_SEQUENCE = -1L ^ (-1L << SEQUENCE_BITS); private static final long MACHINE_ID_SHIFT = SEQUENCE_BITS; private static final long TIMESTAMP_SHIFT = MACHINE_ID_BITS + SEQUENCE_BITS; private static final SnowflakeIdGenerator INSTANCE = new SnowflakeIdGenerator(); private long machineId; private long sequence = 0L; private long lastTimestamp = -1L; public static SnowflakeIdGenerator getInstance() { return INSTANCE; } private SnowflakeIdGenerator() { this.machineId = getMachineId(); } private long getMachineId() { // TODO: 获取机器 ID 的方式,可以是 IP 地址或者其他标识符 return 0L; } public synchronized long generateId() { long timestamp = System.currentTimeMillis(); if (timestamp < lastTimestamp) { throw new RuntimeException("Clock moved backwards. Refusing to generate id"); } if (lastTimestamp == timestamp) { sequence = (sequence + 1) & MAX_SEQUENCE; if (sequence == 0) { timestamp = tilNextMillis(lastTimestamp); } } else { sequence = 0L; } lastTimestamp = timestamp; return ((timestamp - START_TIMESTAMP) << TIMESTAMP_SHIFT) | (machineId << MACHINE_ID_SHIFT) | sequence; } private long tilNextMillis(long lastTimestamp) { long timestamp = System.currentTimeMillis(); while (timestamp <= lastTimestamp) { timestamp = System.currentTimeMillis(); } return timestamp; } } ``` 在使用 Snowflake 算法生成 ID 的时候,需要注意以下几点: 1. 在同一毫秒内,可能会生成多个 ID,因此需要使用一个序列号来标识不同的 ID。由于序列号只有 13 位,所以在高并发的情况下,可能会出现序列号用尽的情况。如果出现这种情况,可以等待下一毫秒,或者使用其他的解决方案。 2. 在使用 Snowflake 算法生成 ID 的时候,每台机器的机器 ID 需要是唯一的。可以使用 IP 地址或者其他标识符来标识不同的机器。如果机器 ID 重复了,可能会生成相同的 ID。 3. Snowflake 算法生成的 ID 是一个 64 位的整数,可以使用字符串来表示。如果需要使用字符串来表示 ID,可以使用 `Long.toString()` 或者其他的解决方案。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值