摘要:Snowflake算法
twitter的雪花算法,是将id按二进制比特位切割,不同的位区间,表示不同的含义,也即是不同位区间
的值生成方式不同,从而生成唯一的id。
如位区间可分为时间位区间、集群位区间、机器位区间、自增位区间,这样可在不同时间内、不同集群、
不同机器间,生成全局唯一的id。
在此以生成64位(即Long型)为例进行介绍(其实区间位可以根据具体的业务需要自行指定)。
1、位区间化分
0 - 41位时间戳 - 5位数据中心标识 - 5位机器标识 - 12位序列号
最高位(即第64位,从右向左数)为符号位,不使用;
41位(第23位到第63位)为时间位,精确到毫秒级,可使用个数为2199023255551个,以毫秒为单位,大约69.5年;可以根据时间进行排序;
5位(第18位到第22位)为集群位,可使用个数为32个;
5位(第13位到第17位)为机器位,可使用个数为32个;
12位(第1位到第12位)为序列号位,即是从0开始自增,可使用个数为4096个;
2、确定时间位开始计算的时间点
本例以2017-10-12 00:00:00开始计时,那么过去掉的时间(从1970-01-01 00:00:00开始)的毫秒数
为1507737600000,取时间时需要减去这段时间。
3、示例代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179
| import java.lang.management.ManagementFactory; import java.net.InetAddress; import java.net.NetworkInterface; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors;
public class IdWorker {
private final static long twepoch = 1288834974657L; private final static long workerIdBits = 5L; private final static long datacenterIdBits = 5L; private final static long maxWorkerId = -1L ^ (-1L << workerIdBits); private final static long maxDatacenterId = -1L ^ (-1L << datacenterIdBits); private final static long sequenceBits = 12L; private final static long workerIdShift = sequenceBits; private final static long datacenterIdShift = sequenceBits + workerIdBits; private final static long timestampLeftShift = sequenceBits + workerIdBits + datacenterIdBits;
private final static long sequenceMask = -1L ^ (-1L << sequenceBits); private static long lastTimestamp = -1L; private long sequence = 0L;
private final long workerId; private final long datacenterId;
public IdWorker(){ this.datacenterId = getDatacenterId(maxDatacenterId); this.workerId = getMaxWorkerId(datacenterId, maxWorkerId); }
public IdWorker(long workerId, long datacenterId) { if (workerId > maxWorkerId || workerId < 0) { throw new IllegalArgumentException(String.format("worker Id can't be greater than %d or less than 0", maxWorkerId)); } if (datacenterId > maxDatacenterId || datacenterId < 0) { throw new IllegalArgumentException(String.format("datacenter Id can't be greater than %d or less than 0", maxDatacenterId)); } this.workerId = workerId; this.datacenterId = datacenterId; }
public synchronized long nextId() { long timestamp = timeGen(); if (timestamp < lastTimestamp) { throw new RuntimeException(String.format("Clock moved backwards. Refusing to generate id for %d milliseconds", lastTimestamp - timestamp)); }
if (lastTimestamp == timestamp) { sequence = (sequence + 1) & sequenceMask; if (sequence == 0) { timestamp = tilNextMillis(lastTimestamp); } } else { sequence = 0L; } lastTimestamp = timestamp; long nextId = ((timestamp - twepoch) << timestampLeftShift) | (datacenterId << datacenterIdShift) | (workerId << workerIdShift) | 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(); }
protected static long getMaxWorkerId(long datacenterId, long maxWorkerId) { StringBuffer mpid = new StringBuffer(); mpid.append(datacenterId); String name = ManagementFactory.getRuntimeMXBean().getName(); if (!name.isEmpty()) {
mpid.append(name.split("@")[0]); }
return (mpid.toString().hashCode() & 0xffff) % (maxWorkerId + 1); }
protected static long getDatacenterId(long maxDatacenterId) { long id = 0L; try { InetAddress ip = InetAddress.getLocalHost(); NetworkInterface network = NetworkInterface.getByInetAddress(ip); if (network == null) { id = 1L; } else { byte[] mac = network.getHardwareAddress(); id = ((0x000000FF & (long) mac[mac.length - 1]) | (0x0000FF00 & (((long) mac[mac.length - 2]) << 8))) >> 6; id = id % (maxDatacenterId + 1); } } catch (Exception e) { System.out.println(" getDatacenterId: " + e.getMessage()); } return id; }
public static void main(String[] args) { final IdWorker idGenerator = new IdWorker(1, 1);
ExecutorService executorService = Executors.newCachedThreadPool(); for (int i = 0; i < 10000; i++) { executorService.execute(new Runnable() { @Override public void run() { long id = idGenerator.nextId(); System.out.println(id); } }); } executorService.shutdown(); } }
|
感谢鼓励