A股上市公司传智教育(股票代码 003032)旗下技术交流社区北京昌平校区

 找回密码
 加入黑马

QQ登录

只需一步,快速开始

 对于分布式系统环境,主键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雪花算法实现
  好,废话不多说,直接上算法实现
[url=][/url]
  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 tilNextMillis(final long lastTimestamp) { 89         long timestamp = this.timeGen(); 90         while (timestamp <= lastTimestamp) { 91             timestamp = this.timeGen(); 92         } 93         return timestamp; 94     } 95 96     private long timeGen() { 97         return System.currentTimeMillis(); 98     } 99 100     /**101      * <p>102      * 获取 maxWorkerId103      * </p>104      */105     protected static long getMaxWorkerId(long datacenterId, long maxWorkerId) {106         StringBuffer mpid = new StringBuffer();107         mpid.append(datacenterId);108         String name = ManagementFactory.getRuntimeMXBean().getName();109         if (!name.isEmpty()) {110             /*111              * GET jvmPid112              */113             mpid.append(name.split("@")[0]);114         }115         /*116          * MAC + PID 的 hashcode 获取16个低位117          */118         return (mpid.toString().hashCode() & 0xffff) % (maxWorkerId + 1);119     }120 121     /**122      * <p>123      * 数据标识id部分124      * </p>125      */126     protected static long getDatacenterId(long maxDatacenterId) {127         long id = 0L;128         try {129             InetAddress ip = InetAddress.getLocalHost();130             NetworkInterface network = NetworkInterface.getByInetAddress(ip);131             if (network == null) {132                 id = 1L;133             } else {134                 byte[] mac = network.getHardwareAddress();135                 id = ((0x000000FF & (long) mac[mac.length - 1])136                         | (0x0000FF00 & (((long) mac[mac.length - 2]) << 8))) >> 6;137                 id = id % (maxDatacenterId + 1);138             }139         } catch (Exception e) {140             System.out.println(" getDatacenterId: " + e.getMessage());141         }142         return id;143     }144 }[url=][/url]


3--测试[url=][/url]
package com.anson;/** * @description: TODO * @author: anson * @Date: 2019/10/7 22:16 * @version: 1.0 */public class snow{    public static  void main(String[] args) throws Exception    {        try        {        IdWorker idw = new IdWorker(1,1);        long ids = idw.nextId();        for(int i=0;i<10000;i++)        {            ids = idw.nextId();            System.out.println(ids);        }        }        catch (Exception ex)        {        }    }}[url=][/url]

结果如下图:

程序生成了19位的有序数字,这个既解决了分布式ID生成唯一性问题,也解决了性能问题,建议系统ID设计都采用该算法生成。

2 个回复

倒序浏览
有任何问题欢迎在评论区留言
回复 使用道具 举报
或者添加学姐微信
DKA-2018
回复 使用道具 举报
您需要登录后才可以回帖 登录 | 加入黑马