• 雪花算法详解及源码分析


    雪花算法的简介:

    雪花算法用来实现全局唯一ID的业务主键,解决分库分表之后主键的唯一性问题,所以就单从全局唯一性来说,其实有很多的解决方法,比如说UUID、数据库的全局表的自增ID

    但是在实际的开发过程中,我们的id除了唯一性以外,还需要去满足有序递增,高性能,高可用,以及需要时间戳等这样一些特征,而雪花算法就是一个比较符合这个一类特征的全局唯一算法。

    雪花算法结构的详解:

    它是一个通过64个bit位 组成的一个long类型的数字,可以将它分为四个部分,根据这四个部分的规则,生成对应的bit位的一个数据,然后组装在一起,形成一个全局的唯一id。

    第一部分:是一个bit:这个是正负号,正常情况下为零,通常无意义

    1)不用 1bit:是不用的

    因为二进制里第一个bit位如果是1,那么都是复数,但是我们生成的id都是正数,所以第一个bit统一都是0

    第二部分:是41个bit:表示的是时间戳

    2)时间戳 41bit:表示的是时间戳,单位是毫秒

    41bit表示的数字多达2^41-1,也就是可以标识2^41-1个毫秒值,换算成年表示就是69年的时间。

    第三、四部分:是5+5个bit:表示的是机房id以及机器id、

    3)+4)工作机器Id 10bit:记录工作机器的id,表示的是这个服务最多可以部署在2^10台机器上,也就是1024台机器。

    但是10bit里5个bit代表机房id,5个bit代表机器id。意思就是最多代表2^个机房(32个机房),每个机房可以代表2^5和机器(32台机器),也可以根据实际情况确定

    第五部分:是12个bit:表示的序号,就是某个机房中某个机器上这一毫秒内同时生成的id的序号,0000 0000 0000

    12bit可以代表的最大正整数是2^12-1=4096,也就是说可以用这个12bit代表的数字来区分同一个毫秒内的4096个不同的id。

    源码:

    1. public class SnowFlakeUtil01 {
    2. // 起始时间戳 (可以自定义)
    3. private final long twepoch = 1288834974657L;
    4. // 机器ID所占的位数
    5. private final long workerIdBits = 5L;
    6. // 数据中心ID所占的位数
    7. private final long datacenterIdBits = 5L;
    8. // 支持的最大机器ID,结果是31 (这个移位算法可以计算最大值:-1L ^ (-1L << workerIdBits))
    9. private final long maxWorkerId = -1L ^ (-1L << workerIdBits);
    10. // 支持的最大数据中心ID,结果是31
    11. private final long maxDatacenterId = -1L ^ (-1L << datacenterIdBits);
    12. // 序列在ID中占的位数
    13. private final long sequenceBits = 12L;
    14. // 机器ID左移位数
    15. private final long workerIdShift = sequenceBits;
    16. // 数据中心ID左移位数
    17. private final long datacenterIdShift = sequenceBits + workerIdBits;
    18. // 时间戳左移位数
    19. private final long timestampLeftShift = sequenceBits + workerIdBits + datacenterIdBits;
    20. // 生成序列的掩码,这里为4095 (0b111111111111=0xfff=4095)
    21. private final long sequenceMask = -1L ^ (-1L << sequenceBits);
    22. // 工作机器ID(0~31)
    23. private long workerId;
    24. // 数据中心ID(0~31)
    25. private long datacenterId;
    26. // 毫秒内序列(0~4095)
    27. private long sequence = 0L;
    28. // 上次生成ID的时间戳
    29. private long lastTimestamp = -1L;
    30. // 构造函数
    31. public SnowFlakeUtil01(long workerId, long datacenterId) {
    32. // 检查workerId是否在合法范围内
    33. if (workerId > maxWorkerId || workerId < 0) {
    34. throw new IllegalArgumentException(String.format("worker Id can't be greater than %d or less than 0", maxWorkerId));
    35. }
    36. // 检查datacenterId是否在合法范围内
    37. if (datacenterId > maxDatacenterId || datacenterId < 0) {
    38. throw new IllegalArgumentException(String.format("datacenter Id can't be greater than %d or less than 0", maxDatacenterId));
    39. }
    40. this.workerId = workerId;
    41. this.datacenterId = datacenterId;
    42. }
    43. /**
    44. * 获得下一个ID (该方法是线程安全的)
    45. * @return SnowflakeId
    46. */
    47. public synchronized long nextId() {
    48. long timestamp = timeGen();
    49. // 如果当前时间小于上一次ID生成的时间戳,说明系统时钟回退过这个时候应当抛出异常
    50. if (timestamp < lastTimestamp) {
    51. throw new RuntimeException(
    52. String.format("Clock moved backwards. Refusing to generate id for %d milliseconds", lastTimestamp - timestamp));
    53. }
    54. // 如果是同一时间生成的,则进行毫秒内序列
    55. if (lastTimestamp == timestamp) {
    56. // 如果毫秒相同,则从0递增生成序列号
    57. sequence = (sequence + 1) & sequenceMask;
    58. // 毫秒内序列溢出
    59. if (sequence == 0) {
    60. // 阻塞到下一个毫秒,获得新的时间戳
    61. timestamp = tilNextMillis(lastTimestamp);
    62. }
    63. }
    64. // 时间戳改变,毫秒内序列重置
    65. else {
    66. sequence = 0L;
    67. }
    68. // 上次生成ID的时间戳
    69. lastTimestamp = timestamp;
    70. // 移位并通过或运算拼到一起组成64位的ID
    71. return ((timestamp - twepoch) << timestampLeftShift) // 时间戳部分
    72. | (datacenterId << datacenterIdShift) // 数据中心部分
    73. | (workerId << workerIdShift) // 机器ID部分
    74. | sequence; // 序列号部分
    75. }
    76. // 阻塞到下一个毫秒,直到获得新的时间戳
    77. protected long tilNextMillis(long lastTimestamp) {
    78. long timestamp = timeGen();
    79. while (timestamp <= lastTimestamp) {
    80. timestamp = timeGen();
    81. }
    82. return timestamp;
    83. }
    84. // 返回当前时间,以毫秒为单位
    85. protected long timeGen() {
    86. return System.currentTimeMillis();
    87. }
    88. // public static void main(String[] args) {
    89. // SnowFlakeUtil snowFlakeUtil = new SnowFlakeUtil(0, 0);
    90. // for (int i = 0; i < 100; i++) {
    91. // long id = snowFlakeUtil.nextId();
    92. // System.out.println(id);
    93. // }
    94. // }
    95. }

  • 相关阅读:
    MySQL中的SHOW FULL PROCESSLIST命令
    element ui的@change事件进行计算
    【微信小程序】小程序的宿主环境
    央企太卷.....来自央企的7个面试题,一个一个生产难题
    在Win11上部署ChatGLM2-6B详细步骤--(下)开始部署
    FTP后门
    【vue】主分支外的一些知识点
    【数据结构】时间复杂度和空间复杂度
    Real3D FlipBook jQuery Plugin 3.41 Crack
    SAP 下载SMW0模板文件并附加数据导入到Excel示例
  • 原文地址:https://blog.csdn.net/m0_73864806/article/details/139421218