java snowflake雪花算法

源码下载地址: https://github.com/twitter-archive/snowflake/tags源码是scala写的…
java版本:https://gitee.com/xyy-kk_admin/data-source/blob/master/SnowflakeIdWorker.java
Twitter的分布式自增ID算法snowflake

概述:

Twitter的snowflake解决了这种需求,最初Twitter把存储系统从MySQL迁移到Cassandra(由Facebook开发一套开源分布式NoSQL数据库系统)。因为Cassandra没有顺序ID生成机制,所以开发了这样一套全局唯一生成服务。

Twitter的分布式雪花算法SnowFlake ,经测试snowflake 每秒能够产生26万个自增可排序的ID

  • Twitter的SnowFlake生成ID能够按照时间有序生成。
  • SnowFlake算法生成ID的结果是一个64bit大小的整数, 为一个Long型(转换成字符串后长度最多19)。
  • 分布式系统内不会产生ID碰撞(由datacenter和workerld作区分)并且效率较高。

分布式系统中,有一些需要使用全局唯一ID的场景, 生成ID的基本要求:

  • 在分布式的环境下必须全局且唯一 。
  • 一般都需要单调递增,因为一般唯一ID都会存到数据库,而Innodb的特性就是将内容存储在主键索引树上的叶子节点而且是从左往右,递增的,所以考虑到数据库性能,一般生成的ID也最好是单调递增。 为了防止ID冲突可以使用36位的UUID,但是UUID有一些缺点, 首先他相对比较长, 另外UUID一般是无序的。
  • 可能还会需要无规则,因为如果使用唯一ID作为订单号这种,为了不然别人知道一天的订单量是多少,就需要这个规则。

结构:

在这里插入图片描述
解析
在这里插入图片描述
SnowFlake可以保证:

所有生成的ID按时间趋势递增。
整个分布式系统内不会产生重复id(因为有DataCenterId和Workerld来做区分)

HUTOOL工具类

引入工具包:

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

最简单的方式:

import cn.hutool.core.lang.Snowflake;
import cn.hutool.core.net.NetUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.log.Log;
import cn.hutool.log.dialect.log4j.Log4jLog;

public class IdGeneratorSnowflake {
    public synchronized long snowflakeId(long workerID,long datacenterID){
        Snowflake snowflake = IdUtil.createSnowflake(workerID,datacenterID);
        return snowflake.nextId();
    }
}

高效 但是复杂的方式:

package service;

import cn.hutool.core.lang.Snowflake;
import cn.hutool.core.net.NetUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.log.Log;
import cn.hutool.log.dialect.log4j.Log4jLog;

/**
 * @author xyy
 * @date 2021年07月09日 17:16
 */
public class IdGeneratorSnowflake {

    Log log = new Log4jLog(IdGeneratorSnowflake.class);

    // 工作设备号
    private static long workerID= 0;
    // 服务 编号
    private static long datacenterID=1;

    // 创建雪花算法对象
    private static Snowflake snowflake = IdUtil.createSnowflake(workerID,datacenterID);


    public IdGeneratorSnowflake(){
        try{
            //获取机器的ipV4 转换成 long 型
            workerID = NetUtil.ipv4ToLong(NetUtil.getLocalhostStr());
            log.info("当前机器的workerID:{}",workerID);
        }catch (Exception e){
            e.printStackTrace();
            log.error("当前机器的workerID获取失败",e);
            workerID = NetUtil.getLocalhostStr().hashCode();
        }
    }

    public static synchronized long snowflakeId(){
        return snowflake.nextId();
    }

    public static void main(String[] args) {
        System.out.println(IdGeneratorSnowflake.snowflakeId());
    }
}


纯java代码

package com.labway.lims.api;

import cn.hutool.core.date.SystemClock;
import cn.hutool.core.util.StrUtil;

import java.io.Serializable;

/**
 * 解决尾数都是偶数的问题,如果位数都是偶数会导致分表不均匀
 */
public class IdGeneratorSnowflake implements Serializable {
    private static final long serialVersionUID = 1L;

    private final long twepoch;
    private final long workerIdBits = 5L;
    private final long dataCenterIdBits = 5L;
     最大支持机器节点数0~31,一共32个
    // 最大支持数据中心节点数0~31,一共32个
    @SuppressWarnings({"PointlessBitwiseExpression", "FieldCanBeLocal"})
    private final long maxWorkerId = -1L ^ (-1L << workerIdBits);
    @SuppressWarnings({"PointlessBitwiseExpression", "FieldCanBeLocal"})
    private final long maxDataCenterId = -1L ^ (-1L << dataCenterIdBits);
    // 序列号12位
    private final long sequenceBits = 12L;
    // 机器节点左移12位
    private final long workerIdShift = sequenceBits;
    // 数据中心节点左移17位
    private final long dataCenterIdShift = sequenceBits + workerIdBits;
    // 时间毫秒数左移22位
    private final long timestampLeftShift = sequenceBits + workerIdBits + dataCenterIdBits;
    @SuppressWarnings({"PointlessBitwiseExpression", "FieldCanBeLocal"})
    private final long sequenceMask = -1L ^ (-1L << sequenceBits);// 4095

    private final long workerId;
    private final long dataCenterId;
    private final boolean useSystemClock;
    private long sequence = 0L;
    private long lastTimestamp = -1L;


    /**
     * @param workerId     工作机器节点id
     * @param dataCenterId 数据中心id
     * @since 5.1.3
     */
    public IdGeneratorSnowflake(long workerId, long dataCenterId) {
        // 2022-05-06 05:06:56
        this.twepoch = 1651784816000L;

        if (workerId > maxWorkerId || workerId < 0) {
            throw new IllegalArgumentException(String.format("worker Id can't be greater than %s or less than 0", maxWorkerId));
        }
        if (dataCenterId > maxDataCenterId || dataCenterId < 0) {
            throw new IllegalArgumentException(String.format("datacenter Id can't be greater than %s or less than 0", maxDataCenterId));
        }
        this.workerId = workerId;
        this.dataCenterId = dataCenterId;
        this.useSystemClock = false;
    }

    /**
     * 根据Snowflake的ID,获取机器id
     *
     * @param id snowflake算法生成的id
     * @return 所属机器的id
     */
    public long getWorkerId(long id) {
        return id >> workerIdShift & ~(-1L << workerIdBits);
    }

    /**
     * 根据Snowflake的ID,获取数据中心id
     *
     * @param id snowflake算法生成的id
     * @return 所属数据中心
     */
    public long getDataCenterId(long id) {
        return id >> dataCenterIdShift & ~(-1L << dataCenterIdBits);
    }

    /**
     * 根据Snowflake的ID,获取生成时间
     *
     * @param id snowflake算法生成的id
     * @return 生成的时间
     */
    public long getGenerateDateTime(long id) {
        return (id >> timestampLeftShift & ~(-1L << 41L)) + twepoch;
    }

    /**
     * 下一个ID
     *
     * @return ID
     */
    public synchronized long nextId() {
        long timestamp = genTime();
        if (timestamp < lastTimestamp) {
            if (lastTimestamp - timestamp < 2000) {
                // 容忍2秒内的回拨,避免NTP校时造成的异常
                timestamp = lastTimestamp;
            } else {
                // 如果服务器时间有问题(时钟后退) 报错。
                throw new IllegalStateException(StrUtil.format("Clock moved backwards. Refusing to generate id for {}ms", lastTimestamp - timestamp));
            }
        }

        if (timestamp == lastTimestamp) {
            sequence = (sequence + 1) & sequenceMask;
            if (sequence == 0) {
                timestamp = tilNextMillis(lastTimestamp);
            }
        } else {
            sequence = (sequence + 1) & sequenceMask;
        }

        lastTimestamp = timestamp;

        return ((timestamp - twepoch) << timestampLeftShift) | (dataCenterId << dataCenterIdShift) | (workerId << workerIdShift) | sequence;
    }
    

	public LinkedList<Long> genIds(int count) {
        count = Math.max(count, 1);
        final LinkedList<Long> ids = new LinkedList<>();
        for (int i = 0; i < count; i++) {
            ids.add(nextId());
        }
        return ids;
    }

    /**
     * 下一个ID(字符串形式)
     *
     * @return ID 字符串形式
     */
    public String nextIdStr() {
        return Long.toString(nextId());
    }

    // ------------------------------------------------------------------------------------------------------------------------------------ Private method start

    /**
     * 循环等待下一个时间
     *
     * @param lastTimestamp 上次记录的时间
     * @return 下一个时间
     */
    private long tilNextMillis(long lastTimestamp) {
        long timestamp = genTime();
        // 循环直到操作系统时间戳变化
        while (timestamp == lastTimestamp) {
            timestamp = genTime();
        }
        if (timestamp < lastTimestamp) {
            // 如果发现新的时间戳比上次记录的时间戳数值小,说明操作系统时间发生了倒退,报错
            throw new IllegalStateException(
                    StrUtil.format("Clock moved backwards. Refusing to generate id for {}ms", lastTimestamp - timestamp));
        }
        return timestamp;
    }

    /**
     * 生成时间戳
     *
     * @return 时间戳
     */
    private long genTime() {
        return this.useSystemClock ? SystemClock.now() : System.currentTimeMillis();
    }
    // ------------------------------------------------------------------------------------------------------------------------------------ Private method end
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值