SnowFlake 算法,是 Twitter 开源的分布式 id 生成算法。其核心思想就是:使用一个 64 bit 的 long 型的数字作为全局唯一 id。在分布式系统中的应用十分广泛,且ID 引入了时间戳,基本上保持自增的。整个分布式系统内不会产生ID碰撞(由datacenter和机器ID作区分), 并且效率较高,经测试,snowflake每秒能够产生26万ID左右,完全满足需要。
雪花算法的原理
雪花算法由四部分组成
第一部分是1个bit,始终为0,不可用。
(因为二进制里第一个 bit 为如果是 1,那么都是负数,但是我们生成的 id 都是正数,所以第一个 bit 统一都是 0。)
第二部分是41bit的时间戳,单位是毫秒。
(41 bit 可以表示的数字多达 2^41 - 1,也就是可以标识 2 ^ 41 - 1 个毫秒值,换算成年就是表示 69 年的时间。)
第三部分是10bit的工作机器ID,由5个bit的机房ID和5个bit的机器ID组成。
(代表 2 ^ 5 个机房(32 个机房),每个机房里可以代表 2 ^ 5 个机器(32 台机器),也就是代表可以部署在1024台机器上。)
第四部分是12个bit的序号,就是某个机房某台机器上这一毫秒内同时生成的 id 的序号,0000 00000000。
(12 bit 可以代表的最大正整数是 2 ^ 12 - 1 = 4096,也就是说可以用这个 12 bit 代表的数字来区分同一个毫秒内的 4096 个不同的 id。)
雪花算法的实现 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 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; } }