简单介绍下雪花算法, 以及Java版雪花算法的代码.
仅仅是一个最简单版本, 更深层次的指针回拨等. 相当于在开发过成功可以先使用.
尽量还是使用统一的分布式流水号生成系统, 保证流水号全局唯一.
雪花算法
1
| 0 0000000000 0000000000 0000000000 0000000000 0 00000 00000 000000000000
|
使用64位long型数字作为全局唯一id
1位 无意义 0
41位 时间戳
5位 机房id
5位 机器id
12位自增序号 表示同一时间同一机房同一机器生成的序列号
第一位为什么无意义
二进制中 第一位代表符号位, 默认 0 表示生成的序列号为正数
41位时间戳
41位最大能表示 2^41-1 的数字. 毫秒值 69.7年
(2^41-1)/1000/60/60/24
当时间大于69.7即时间戳差值大于 2199023255551, 会开始出现负值流水号
10位
机房id+机器id 2^10 1024台机器
1 2 3 4 5 6 7 8 9 10 11 12 13 14
|
private static final long MACHINE_BIT = 8; private static final long DATA_CENTER_BIT = 2;
private static final long DATA_CENTER_ID = 1; private static long address; static { InetAddress localIp = IpUtils.getLocalIp(); address = localIp.getAddress()[3] & 0xff; log.info("当前系统的 address 为: {}", address); }
|
12位序列号
表示同一毫秒内生成的id 2^12-1 个正整数
SnowFlake每秒能够产生26万ID左右
优点:
生成ID时不依赖于DB,完全在内存生成,高性能高可用。
ID呈趋势递增,后续插入索引树的时候性能较好。
缺点:
依赖于系统时钟的一致性。如果某台机器的系统时钟回拨,有可能造成ID冲突,或者ID乱序
SerialNumber
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
| public class SerialNumber {
private static final long START_STAMP = 1514736000000L;
private static final long SEQUENCE_BIT = 12; private static final long MACHINE_BIT = 8; private static final long DATA_CENTER_BIT = 2;
private static final long MAX_DATA_CENTER_NUM = ~(-1L << DATA_CENTER_BIT); private static final long MAX_MACHINE_NUM = ~(-1L << MACHINE_BIT); private static final long MAX_SEQUENCE = ~(-1L << SEQUENCE_BIT);
private static final long MACHINE_LEFT = SEQUENCE_BIT; private static final long DATA_CENTER_LEFT = SEQUENCE_BIT + MACHINE_BIT; private static final long TIME_STAMP_LEFT = DATA_CENTER_LEFT + DATA_CENTER_BIT;
private static final long DATA_CENTER_ID = 1; private static long address; private long sequence = 0L; private long lastStamp = -1L;
private static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyMMdd");
static { InetAddress localIp = IpUtils.getLocalIp(); address = localIp.getAddress()[3] & 0xff; log.info("当前系统的 address 为: {}", address); }
private synchronized long nextId() { long currStamp = getNewStamp(); if (currStamp < lastStamp) { throw new RuntimeException("Clock moved backwards. Refusing to generate id"); }
if (currStamp == lastStamp) { sequence = (sequence + 1) & MAX_SEQUENCE; if (sequence == 0L) { currStamp = getNextMill(); } } else { sequence = 0L; }
lastStamp = currStamp; return (currStamp - START_STAMP) << TIME_STAMP_LEFT | DATA_CENTER_ID << DATA_CENTER_LEFT | address << MACHINE_LEFT | sequence; }
private long getNextMill() { long mill = getNewStamp(); while (mill <= lastStamp) { mill = getNewStamp(); } return mill; }
private long getNewStamp() { return System.currentTimeMillis(); } }
|
IpUtils
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
| import java.net.*; import java.util.Enumeration;
public class IpUtils {
public static InetAddress getLocalIp() { try { for (Enumeration<NetworkInterface> e = NetworkInterface.getNetworkInterfaces(); e.hasMoreElements(); ) { NetworkInterface item = e.nextElement(); for (InterfaceAddress address : item.getInterfaceAddresses()) { if (item.isLoopback() || !item.isUp()) { continue; } if (address.getAddress() instanceof Inet4Address) { return address.getAddress(); } } } return InetAddress.getLocalHost(); } catch (SocketException | UnknownHostException e) { throw new RuntimeException(e); } }
}
|