4

雪花算法详解_大鱼的技术博客_51CTO博客

 1 year ago
source link: https://blog.51cto.com/learningfish/5576396
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

SnowFlake 介绍

  • SnowFlake 中文意思为雪花,故称为雪花算法。
  • 最早是 Twitter 公司在其内部用于分布式环境下生成唯一 ID。
  • 2014年开源 scala 语言版本。
雪花算法详解_时间戳
  • 首位: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整合使用雪花算法

结构如下:

雪花算法详解_时间戳_02

第一步:配置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);
		}
	}

}
雪花算法详解_时间戳_03

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK