• 分布式主键解决方案之--Snowflake雪花算法


    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 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      * 获取 maxWorkerId
    103      * </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 jvmPid
    112              */
    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 }

    3--测试

    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)
            {
    
            }
    
        }
    }

    结果如下图:

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

  • 相关阅读:
    jquery UI_tabs
    乔布斯传
    微信小程序
    天气预报相关参数
    ASP.NET MVC TempData使用心得
    jquery.restrictFieldLength.js
    join Linq
    SQL_关联映射
    介绍几个好用的vs插件
    DependencyResolver.Current
  • 原文地址:https://www.cnblogs.com/yanghj/p/11632811.html
Copyright © 2020-2023  润新知