分布式雪花算法生成唯一ID

雪花算法组成
在这里插入图片描述
10bit工作进程位:用来记录工作机器的id,包括5位datacenterId和5位workerId;5位(bit)可以表示的最大正整数是31,即可以用0,1,2,3,…31这32个数字,来表示不同的datacenterId和workerId

Hutool工具包

<dependency>
    <groupId>cn.hutool</groupId>
    <artifactId>hutool-all</artifactId>
    <version>5.7.16</version>
</dependency>

Snowflake需自行保证单例,否则多个对象生成ID会导致重复

代码

package com.lyods.base.utils;

import cn.hutool.core.lang.Snowflake;
import cn.hutool.core.net.NetUtil;
import cn.hutool.core.util.IdUtil;

import javax.annotation.PostConstruct;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * 
 * @creator linhy
 * @date 2021-11-16
 * @descrption 获取雪花算法ID
 *
 */
public class SnowFlakeUtil {

    private final static Logger log = LoggerFactory.getLogger(SnowFlakeUtil.class);

    private static long workerId = 0;

    private volatile static Snowflake snowflake = null;
    private volatile static SnowFlakeUtil sfu = null;

    /**
     * 单例
     */
    private SnowFlakeUtil() {
    }

    public static SnowFlakeUtil getInstance() {
        if (null == sfu) {
            synchronized (SnowFlakeUtil.class) {
                if (null == sfu) {
                    sfu = new SnowFlakeUtil();
                }
            }
        }
        return sfu;
    }

    /**
     * 获取ID
     * @return
     */
    public long snowflakeId() {
        if (null == snowflake) {
            snowflake = getSnowflake();
        }
        return snowflake.nextId();
    }

    /**
     * 获取ID(自定义工作机器ID)
     * @param workerId     终端ID
     * @param dataCenterId 数据中心ID
     * @return
     */
    public long snowflakeId(long workerId, long dataCenterId) {
        if (null == snowflake) {
            snowflake = getSnowflake(workerId, dataCenterId);
        }
        return snowflake.nextId();
    }

    /**
     * Snowflake带参对象获取
     * @return
     */
    private synchronized Snowflake getSnowflake(long workerId, long dataCenterId) {
        snowflake = IdUtil.getSnowflake(workerId, dataCenterId);
        return snowflake;
    }

    /**
     * Snowflake无参对象获取
     * @return
     */
    private synchronized Snowflake getSnowflake() {
        snowflake = new Snowflake();
        return snowflake;
    }

    @PostConstruct
    public void init() {
        try {
            workerId = NetUtil.ipv4ToLong(NetUtil.getLocalhostStr());
            log.info("当前机器的workId: {}", workerId);
        } catch (Exception e) {
            e.printStackTrace();
            log.error("当前机器的workId获取失败", e);
            workerId = NetUtil.getLocalhostStr().hashCode();
        }
    }

}

线程池30线程测试

ExecutorService executorService = Executors.newFixedThreadPool(5);
Stream.iterate(0, x -> x + 1).limit(30).forEach(x -> {
    executorService.submit(() -> {
        long id = SnowFlakeUtil.getInstance().snowflakeId();
        System.out.println(id);
    });
});

结果
在这里插入图片描述

  • 优点
    时间戳位在高位,ID趋势递增(几千页的分页下可以根据最后一条数据ID进行分页提高效率),生成ID性能高,不依赖第三方系统,分布式系统内不会产生ID碰撞(由datacenter和workerId作区分)
  • 缺点
    回拨本机时间可能导致时间戳相同,ID重复,分布式下不同机器可能时间不完全同步
本图文内容来源于网友网络收集整理提供,作为学习参考使用,版权属于原作者。
THE END
分享
二维码
< <上一篇
下一篇>>