分布式ID生成-雪花算法

雪花算法的原始版本是scala版,用于生成分布式ID(纯数字,时间顺序),订单编号等。

自增ID:对于数据敏感场景不宜使用,且不适合于分布式场景。
GUID:采用无意义字符串,数据量增大时造成访问过慢,且不宜排序。

在这里插入图片描述
算法描述:
雪花算法是一款开源的ID算法。主要的组成:
最高位是符号位,始终为0,不可用。
41位的时间序列,精确到毫秒级,41位的长度可以使用69年。时间位还有一个很重要的作用是可以根据时间进行排序。
10位的机器标识,10位的长度最多支持部署1024个节点。
12位的计数序列号,序列号即一系列的自增id,可以支持同一节点同一毫秒生成多个ID序号,12位的计数序列号支持每个节点每毫秒产生4096个ID序号

package com.xiaoxiao.utils;

import java.lang.management.ManagementFactory;
import java.net.InetAddress;
import java.net.NetworkInterface;

/**
 * <p>鍚嶇О锛欼dWorker.java</p>
 * <p>鎻忚堪锛氬垎甯冨紡鑷闀縄D</p>
 * <pre>
 *     Twitter鐨� Snowflake銆�JAVA瀹炵幇鏂规
 * </pre>
 * 鏍稿績浠g爜涓哄叾IdWorker杩欎釜绫诲疄鐜帮紝鍏跺師鐞嗙粨鏋勫涓嬶紝鎴戝垎鍒敤涓�涓�0琛ㄧず涓�浣嶏紝鐢ㄢ�斿垎鍓插紑閮ㄥ垎鐨勪綔鐢細
 * 1||0---0000000000 0000000000 0000000000 0000000000 0 --- 00000 ---00000 ---000000000000
 * 鍦ㄤ笂闈㈢殑瀛楃涓蹭腑锛岀涓�浣嶄负鏈娇鐢紙瀹為檯涓婁篃鍙綔涓簂ong鐨勭鍙蜂綅锛夛紝鎺ヤ笅鏉ョ殑41浣嶄负姣绾ф椂闂达紝
 * 鐒跺悗5浣峝atacenter鏍囪瘑浣嶏紝5浣嶆満鍣↖D锛堝苟涓嶇畻鏍囪瘑绗︼紝瀹為檯鏄负绾跨▼鏍囪瘑锛夛紝
 * 鐒跺悗12浣嶈姣鍐呯殑褰撳墠姣鍐呯殑璁℃暟锛屽姞璧锋潵鍒氬ソ64浣嶏紝涓轰竴涓狶ong鍨嬨��
 * 杩欐牱鐨勫ソ澶勬槸锛屾暣浣撲笂鎸夌収鏃堕棿鑷鎺掑簭锛屽苟涓旀暣涓垎甯冨紡绯荤粺鍐呬笉浼氫骇鐢烮D纰版挒锛堢敱datacenter鍜屾満鍣↖D浣滃尯鍒嗭級锛�
 * 骞朵笖鏁堢巼杈冮珮锛岀粡娴嬭瘯锛宻nowflake姣忕鑳藉浜х敓26涓嘔D宸﹀彸锛屽畬鍏ㄦ弧瓒抽渶瑕併��
 * <p>
 * 64浣岻D (42(姣)+5(鏈哄櫒ID)+5(涓氬姟缂栫爜)+12(閲嶅绱姞))
 *闆姳绠楁硶ID鐢熸垚鍣�
 * @author Polim
 */
public class IdWorker
{
    // 鏃堕棿璧峰鏍囪鐐癸紝浣滀负鍩哄噯锛屼竴鑸彇绯荤粺鐨勬渶杩戞椂闂达紙涓�鏃︾‘瀹氫笉鑳藉彉鍔級
    private final static long twepoch = 1288834974657L;
    // 鏈哄櫒鏍囪瘑浣嶆暟
    private final static long workerIdBits = 5L;
    // 鏁版嵁涓績鏍囪瘑浣嶆暟
    private final static long datacenterIdBits = 5L;
    // 鏈哄櫒ID鏈�澶у��
    private final static long maxWorkerId = -1L ^ (-1L << workerIdBits);
    // 鏁版嵁涓績ID鏈�澶у��
    private final static long maxDatacenterId = -1L ^ (-1L << datacenterIdBits);
    // 姣鍐呰嚜澧炰綅
    private final static long sequenceBits = 12L;
    // 鏈哄櫒ID鍋忓乏绉�12浣�
    private final static long workerIdShift = sequenceBits;
    // 鏁版嵁涓績ID宸︾Щ17浣�
    private final static long datacenterIdShift = sequenceBits + workerIdBits;
    // 鏃堕棿姣宸︾Щ22浣�
    private final static long timestampLeftShift = sequenceBits + workerIdBits + datacenterIdBits;

    private final static long sequenceMask = -1L ^ (-1L << sequenceBits);
    /* 涓婃鐢熶骇id鏃堕棿鎴� */
    private static long lastTimestamp = -1L;
    // 0锛屽苟鍙戞帶鍒�
    private long sequence = 0L;

    private final long workerId;
    // 鏁版嵁鏍囪瘑id閮ㄥ垎
    private final long datacenterId;

    public IdWorker()
    {
        this.datacenterId = getDatacenterId(maxDatacenterId);
        this.workerId = getMaxWorkerId(datacenterId, maxWorkerId);
    }

    /**
     * @param workerId     宸ヤ綔鏈哄櫒ID
     * @param datacenterId 搴忓垪鍙�
     */
    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;
    }

    /**
     * 鑾峰彇涓嬩竴涓狪D
     *
     * @return
     */
    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)
        {
            // 褰撳墠姣鍐咃紝鍒�+1
            sequence = (sequence + 1) & sequenceMask;
            if (sequence == 0)
            {
                // 褰撳墠姣鍐呰鏁版弧浜嗭紝鍒欑瓑寰呬笅涓�绉�
                timestamp = tilNextMillis(lastTimestamp);
            }
        }
        else
        {
            sequence = 0L;
        }
        lastTimestamp = timestamp;
        // ID鍋忕Щ缁勫悎鐢熸垚鏈�缁堢殑ID锛屽苟杩斿洖ID
        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();
    }

    /**
     * <p>
     * 鑾峰彇 maxWorkerId
     * </p>
     */
    protected static long getMaxWorkerId(long datacenterId, long maxWorkerId)
    {
        StringBuffer mpid = new StringBuffer();
        mpid.append(datacenterId);
        String name = ManagementFactory.getRuntimeMXBean().getName();
        if (!name.isEmpty())
        {
            /*
             * GET jvmPid
             */
            mpid.append(name.split("@")[0]);
        }
        /*
         * MAC + PID 鐨� hashcode 鑾峰彇16涓綆浣�
         */
        return (mpid.toString().hashCode() & 0xffff) % (maxWorkerId + 1);
    }

    /**
     * <p>
     * 鏁版嵁鏍囪瘑id閮ㄥ垎
     * </p>
     */
    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;
    }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值