0
点赞
收藏
分享

微信扫一扫

雪花算法详解

SnowFlake 介绍

  • SnowFlake 中文意思为雪花,故称为雪花算法。
  • 最早是 Twitter 公司在其内部用于分布式环境下生成唯一 ID。
  • 2014年开源 scala 语言版本。

组成部分

snowflake.png

  • 首位:1bit,固定为0。
  • 时间戳:41bit,((2^41 ) - 1) / (1000x60x60x24x365) 大约可以使用69年。
  • 机器码: 10bit,一般前5bit用户机房,后5bit用于服务器,共可部署2^5 x 2^5 = 1024 台服务器。
  • 序列号:12bit,同一毫秒时间戳,通过序列号来递增区分,1ms可以容纳 (2^12) -1= 4095个id,超过则获取下一毫秒。

java算法实现雪花算法

package com.example.demo.utils;

/**
 * @author 谢阳
 * @version 1.8.0_131
 * @date 2022/08/12 20:30:00
 * @description 雪花算法
 */
public class SnowFlake {

    /**
     * 组成部分 最高符号位 + 时间戳 + (机房id+机房id) + 序列号
     */

    // 修复时间戳 2022-08-12 20:30:00
    private static final long FIX_TIME_STAMP = 1660307400L;

    // 机房id
    private final long computerRoomId;

    // 机器id
    private final long machineId;

    // 序列号
    private long sequence = 0L;

    /**
     * 所占用的bit个数
     */

    // 时间戳41bit

    // 5bit机房id
    private static final long COMPUTER_ROOM_BIT_CNT = 5L;

    // 5bit机器id
    private static final long MACHINE_BIT_CNT = 5L;

    // 12bit序列号
    private static final long SEQUENCE_BIT_CNT = 12L;

    /**
     * 位移的位数
     */

    // 机器id 左移12位
    private static final long MACHINE_ID_SHIFT = SEQUENCE_BIT_CNT;

    // 机房id 左移17位
    private static final long COMPUTER_ROOM_ID_SHIFT = MACHINE_ID_SHIFT + MACHINE_BIT_CNT;

    // 时间戳 左移22位
    private final static long TIME_STAMP_SHIFT = COMPUTER_ROOM_ID_SHIFT + COMPUTER_ROOM_BIT_CNT;

    /**
     * 聚合信息
     */

    // 支持最大的机房id机房id  5bit
    private static final long MAX_COMPUTER_ROOM_ID = ~(-1 << COMPUTER_ROOM_BIT_CNT);

    // 支持最大的机器id 5bit
    private static final long MAX_MACHINE_ID = ~(-1 << MACHINE_BIT_CNT);

    // 序列号支持的最大的个数 12bit
    private static final long SEQUENCE_MASK = ~(-1 << SEQUENCE_BIT_CNT);

    // 上一次生成的时间戳
    private long lastTimeStamp = -1L;

    /**
     * @param computerRoomId 机房id
     * @param machineId      机器id
     */
    public SnowFlake(long computerRoomId, long machineId) {
        if (computerRoomId < 0 || computerRoomId > MAX_COMPUTER_ROOM_ID) {
            throw new IllegalArgumentException("computerRoomId 不在范围");
        }

        if (machineId < 0 || machineId > MAX_MACHINE_ID) {
            throw new IllegalArgumentException("computerRoomId 不在范围");
        }

        this.computerRoomId = computerRoomId;
        this.machineId = machineId;
    }

    /**
     * @return 返回毫秒级时间戳
     */
    private long getCurrentTime() {
        return System.currentTimeMillis();
    }

    /**
     * @return 雪花刷法生成 id
     */
    public synchronized long getNextId() {
        // 拿到时间戳
        long currentTimeStamp = getCurrentTime();

        // 时间戳回拨问题
        if (currentTimeStamp < lastTimeStamp) {
            throw new RuntimeException(
                    String.format("可能出现服务器时钟回拨问题,请检查服务器时间。当前服务器时间戳:%d,上一次使用时间戳:%d", currentTimeStamp,
                            lastTimeStamp));
        }

        // 时间为同一毫秒时间,sequence + 1
        if (currentTimeStamp == lastTimeStamp) {
            // 序列号 + 1
            sequence = (sequence + 1) & SEQUENCE_MASK;
            // 序列号用完
            if (sequence == 0) {
                // 获取下一个毫秒级
                currentTimeStamp = getNextMillis();
            }
        } else {
            sequence = 0;
        }

        // 记录上一次时间戳
        lastTimeStamp = currentTimeStamp;
        // 生成唯一id
        return ((currentTimeStamp - FIX_TIME_STAMP) << TIME_STAMP_SHIFT) |
                (computerRoomId << COMPUTER_ROOM_ID_SHIFT) |
                (machineId << MACHINE_ID_SHIFT) |
                sequence;
    }

    /**
     * @return 下一毫秒
     */
    private long getNextMillis() {
        long currentTimeStamp = getCurrentTime();
        while (currentTimeStamp <= lastTimeStamp) {
            currentTimeStamp = getCurrentTime();
        }
        return currentTimeStamp;
    }

}

springBoot整合使用雪花算法

结构如下:
image.png

第一步:配置yaml

SnowFlake:
  computerRoomId: 0
  machineId: 0

第二步:编写SnowFlake雪花算法

略.

第三步:编写SnowFlakeConfig配置文件

package com.example.demo.config;

import com.example.demo.utils.SnowFlake;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * @author 谢阳
 * @version 1.8.0_131
 * @date 2022/8/12 21:25
 * @description
 */
@Slf4j
@Configuration
public class SnowFlakeConfig {
    @Value("${SnowFlake.computerRoomId}")
    private long computerRoomId;

    @Value("${SnowFlake.machineId}")
    private long machineId;

    @Bean
    public SnowFlake snowFlake() {
        return new SnowFlake(computerRoomId,machineId);
    }
}

测试代码如下:

package com.example.demo;

import com.example.demo.utils.SnowFlake;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

@SpringBootTest
class DemoApplicationTests {

    @Autowired
    SnowFlake snowFlake;

    @Test
    void contextLoads() {
        for (int i = 0; i < 5; i++) {
            long flakeId = snowFlake.getNextId();
            System.out.println(i + " = " +flakeId);
        }
    }

}

image.png

举报

相关推荐

0 条评论