0--前言
对于分布式系统环境,主键ID的设计很关键,什么自增intID那些是绝对不用的,比较早的时候,大部分系统都用UUID/GUID来作为主键,优点是方便又能解决问题,缺点是插入时因为UUID/GUID的不规则导致每插入一条数据就需要重新排列一次,性能低下;也有人提出用UUID/GUID转long的方式,可以很明确的告诉你,这种方式long不能保证唯一,大并发下会有重复long出现,所以也不可取,这个主键设计问题曾经是很多公司系统设计的一个头疼点,所以大部分公司愿意牺牲一部分性能而直接采用简单粗暴的UUID/GUID来作为分布式系统的主键;
twitter开源了一个snowflake算法,俗称雪花算法;就是为了解决分布式环境下生成不同ID的问题;该算法会生成19位的long型有序数字,MySQL中用bigint来存储(bigint长度为20位);该算法应该是目前分布式环境中主键ID最好的解决方案之一了;
1--snowflake雪花算法实现
好,废话不多说,直接上算法实现
1 package com.anson; 2 3 import java.lang.management.ManagementFactory; 4 import java.net.InetAddress; 5 import java.net.NetworkInterface; 6 7 //雪花算法代码实现 8 public class IdWorker { 9 // 时间起始标记点,作为基准,一般取系统的最近时间(一旦确定不能变动) 10 private final static long twepoch = 1288834974657L; 11 // 机器标识位数 12 private final static long workerIdBits = 5L; 13 // 数据中心标识位数 14 private final static long datacenterIdBits = 5L; 15 // 机器ID最大值 16 private final static long maxWorkerId = -1L ^ (-1L << workerIdBits); 17 // 数据中心ID最大值 18 private final static long maxDatacenterId = -1L ^ (-1L << datacenterIdBits); 19 // 毫秒内自增位 20 private final static long sequenceBits = 12L; 21 // 机器ID偏左移12位 22 private final static long workerIdShift = sequenceBits; 23 // 数据中心ID左移17位 24 private final static long datacenterIdShift = sequenceBits + workerIdBits; 25 // 时间毫秒左移22位 26 private final static long timestampLeftShift = sequenceBits + workerIdBits + datacenterIdBits; 27 28 private final static long sequenceMask = -1L ^ (-1L << sequenceBits); 29 /* 上次生产id时间戳 */ 30 private static long lastTimestamp = -1L; 31 // 0,并发控制 32 private long sequence = 0L; 33 34 private final long workerId; 35 // 数据标识id部分 36 private final long datacenterId; 37 38 public IdWorker(){ 39 this.datacenterId = getDatacenterId(maxDatacenterId); 40 this.workerId = getMaxWorkerId(datacenterId, maxWorkerId); 41 } 42 /** 43 * @param workerId 44 * 工作机器ID 45 * @param datacenterId 46 * 序列号 47 */ 48 public IdWorker(long workerId, long datacenterId) { 49 if (workerId > maxWorkerId || workerId < 0) { 50 throw new IllegalArgumentException(String.format("worker Id can't be greater than %d or less than 0", maxWorkerId)); 51 } 52 if (datacenterId > maxDatacenterId || datacenterId < 0) { 53 throw new IllegalArgumentException(String.format("datacenter Id can't be greater than %d or less than 0", maxDatacenterId)); 54 } 55 this.workerId = workerId; 56 this.datacenterId = datacenterId; 57 } 58 /** 59 * 获取下一个ID 60 * 61 * @return 62 */ 63 public synchronized long nextId() { 64 long timestamp = timeGen(); 65 if (timestamp < lastTimestamp) { 66 throw new RuntimeException(String.format("Clock moved backwards. Refusing to generate id for %d milliseconds", lastTimestamp - timestamp)); 67 } 68 69 if (lastTimestamp == timestamp) { 70 // 当前毫秒内,则+1 71 sequence = (sequence + 1) & sequenceMask; 72 if (sequence == 0) { 73 // 当前毫秒内计数满了,则等待下一秒 74 timestamp = tilNextMillis(lastTimestamp); 75 } 76 } else { 77 sequence = 0L; 78 } 79 lastTimestamp = timestamp; 80 // ID偏移组合生成最终的ID,并返回ID 81 long nextId = ((timestamp - twepoch) << timestampLeftShift) 82 | (datacenterId << datacenterIdShift) 83 | (workerId << workerIdShift) | sequence; 84 85 return nextId; 86 } 87 88 private long ti