在雪花算法自定义解决时钟回拨问题一文中,对雪花算法的时钟回拨解决思路进行了说明,由于顺序号保存在内存中,每次启动都是从初始值开始,在特定场景下,比如停止服务后进行了时钟回拨,在理论上,还是可能出现序列号重复的情况。

这里将序列号持久化到本地磁盘文件中,这样下次启动时,首先会读取之前保存的持久化文件,获取序列号,而不是直接从固定值(比如1)开始,这样就算回拨了时间,只要顺序号还是持续增加的,就不会出现序列号相同的情况。

package com.demo.server.config;import cn.hutool.core.net.NetUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.io.ClassPathResource;import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;public class CustomSnowFlake {private static Logger log = LoggerFactory.getLogger(CustomSnowFlake.class);// 2020-01-01 00:00:00 对应的秒private final static long beginTs = 1577808000L;// 顺序号最大值(大约419万)private final static long maxSequence = 4194300L;// 最大时钟回拨(秒),4194秒,大约69分钟private final static long maxTimeback = 4194L;private long lastTs = 0L;private long processId;private int processIdBits = 10;private long sequence = 1L;private int sequenceBits = 22;public CustomSnowFlake() throws IOException {String ipAddr = NetUtil.getLocalhostStr();log.info("当前机器的ipAddr:" + ipAddr);Long workerId = NetUtil.ipv4ToLong(ipAddr);workerId = workerId % 1024;log.info("当前机器的workId:" + workerId);this.processId = workerId;}/*** 根据已知的workerId和流水号进行初始化* @param workerId* @param sequence*/public CustomSnowFlake(Long workerId, Long sequence) {this.processId = workerId;this.sequence = sequence;}/**** @param processId*/public CustomSnowFlake(long processId) {if (processId > ((1 << processIdBits) - 1)) {throw new RuntimeException("进程ID超出范围,设置位数" + processIdBits + ",最大" + ((1 << processIdBits) - 1));}this.processId = processId;}/*** 文件不存在时,不需要报错*/public void restoreCheckPoint() {String fileName = "/snowflake.properties";ClassPathResource resource = new ClassPathResource(fileName);try {String confPath = resource.getURI().getPath();
//          File confFile = new File(confPath);
//          InputStream inputStream = new FileInputStream(confFile);InputStream inputStream = resource.getInputStream();// inputStream = this.getClass().getResourceAsStream(fileName);// 设置1KB的缓冲区byte buf[] = new byte[1024];int read = inputStream.read(buf);inputStream.close();String buffInfo = new String(buf);String confInfo = buffInfo.substring(0, read);if (confInfo.startsWith("sequence=")) {String splits[] = confInfo.split("=");String sequence = splits[1];this.sequence = Long.parseLong(sequence);}} catch (Exception e) {e.printStackTrace();}}/*** 文件不存在时,不影响正常使用* 只是不能持久化顺序号*/public void saveCheckPoint() {String fileName = "/snowflake.properties";ClassPathResource resource = new ClassPathResource(fileName);try {String confPath = resource.getURI().getPath();System.out.println("confPath = " + confPath);File confFile = new File(confPath);FileOutputStream outputStream = new FileOutputStream(confFile);String configInfo = "sequence=" + sequence;outputStream.write(configInfo.getBytes(StandardCharsets.UTF_8));outputStream.close();} catch (Exception e) {e.printStackTrace();}}/*** 获取当前时间(秒为单位)* @return*/protected long timeGen() {return System.currentTimeMillis() / 1000;}/*** 生成一个Id* @return*/public synchronized long nextId() {// 获取当前时间(秒为单位)long ts = timeGen();// 刚刚生成的时间戳比上次的时间戳还小,出错long tempDiff = lastTs - ts;if (tempDiff >= maxTimeback) {log.warn("时钟回拨超过4194秒,存在Id重复风险");}sequence = sequence + 1;if(sequence >= maxSequence){sequence = 1;}// 更新lastTs时间戳lastTs = ts;long timeDiff = ts - beginTs;return (timeDiff << (processIdBits + sequenceBits)) | (processId << sequenceBits) | sequence;}public static void main(String[] args) throws Exception {// TODO Auto-generated method stubCustomSnowFlake ig = new CustomSnowFlake();ig.restoreCheckPoint();for (int i = 0; i < 10; i++) {System.out.println(ig.nextId());ig.saveCheckPoint();Thread.sleep(1000);}}
}

根据情况,还可以进行一定的优化,比如不一定每次生成id都进行持久化,而是当生成id次数达到一定数量或者间隔一定时间(比如3秒或5秒)以后,再进行持久化,这样就可以提高效率。当然这样做的结果,就可能有部分序列号没有成功保存的情况。因为序列号中的时间因子是每秒编号,所有程序重新启动后,往往时间就和之前的不相同了。

对于并发量小的情况,每次生成id都进行持久化,对性能和效率的影响也不大,对并发量大的情况,就需要考虑采用间隔一定数量或时间的方式来进行优化了。

雪花算法通过顺序号持久化解决时钟回拨相关推荐

  1. 雪花算法snowflake分布式id生成原理详解,以及对解决时钟回拨问题几种方案讨论

    文章目录 一.前言 二.雪花算法snowflake 1.基本定义 2.snowflake的优缺点 三.Java代码实现snowflake 1.组装生成id 2.计算最大值的几种方式 3.反解析ID 4 ...

  2. 分布式全局唯一ID生成算法(改进的雪花算法——解决时钟回拨问题)

    改进的雪花算法--解决时钟回拨问题 原创 公众号: 软件设计活跃区 改进的雪花算法--姑且称为梨花算法吧(忽如一夜春风来,千树万树梨花开). 改进目标:解决雪花算法的时钟回拨问题:部分避免机器id重复 ...

  3. 雪花算法-Java实现-解决时钟回拨的一种方法

    背景: 前不久发生了一次严重的生产事件, 与雪花算法有关,但不是雪花算法的问题 具体问题参考代码main中的注释, 结论如下 序列可以使用69年, 序列的长度变化是这样的, 假设以当前时间为初始化值 ...

  4. 记一次错误使用雪花算法引起的数据库主键冲突和解决时钟回拨问题

    在分布式系统中,有一些需要使用全局唯一 ID 的场景,这种时候为了防止 ID 冲突可以使用 36 位的 UUID,但是 UUID 有一些缺点,首先他相对比较长,另外 UUID 一般是无序的 有些时候我 ...

  5. 雪花算法解决时钟回拨问题

    SnowFlake算法 据国家大气研究中心的查尔斯·奈特称,一般的雪花大约由10^19个水分子组成.在雪花形成过程中,会形成不同的结构分支,所以说大自然中不存在两片完全一样的雪花,每一片雪花都拥有自己 ...

  6. 分布式下使用雪花算法生成全局ID及解决时钟回拨问题

    简介 雪花算法是 64 位 的二进制,一共包含了四部分: 1位是符号位,也就是最高位,始终是0,没有任何意义,因为要是唯一计算机二进制补码中就是负数,0才是正数 41位是时间戳,具体到毫秒,41位的二 ...

  7. 雪花算法自定义解决时钟回拨问题

    雪花算法默认算法生成一个 64bit的长整型(Long)数据.主要由 4部分组成,1bit符号位.41bit时间戳位.10bit工作进程位以及 12bit 序列号位. 正常情况下,该算法可以保证系统中 ...

  8. 面试题:雪花算法(SnowFlake)如何解决时钟回拨问题

    1. 雪花算法 雪花算法是一种分布式ID生成算法,首先它生产的是一个64bit位的ID,这64bit位中划分成多段: 第1个bit位:保留位,无实际作用 第2-42的bit位:这41位表示时间戳,精确 ...

  9. 雪花算法生成分布式ID的时间回拨问题处理

    一般方法 1.直接抛异常 2.延迟等待到最新时间(需要回拨时间比较短) 3.采用历史最大时间 package com.zjq.javabasic.algorithm;/*** @description ...

最新文章

  1. 从浏览器地址栏输入网址,到网页彻底打开,中间都发生了什么?
  2. PAT甲级 -- 1148 Werewolf - Simple Version (20 分)
  3. 失物招领php_新奥尔良圣徒队是否增加了失物招领?
  4. 护卫神怎么增加php版本_护卫神php套件 php版本升级方法(php5.5.24)
  5. Java 面试 80% 的人都会踩这些坑,你知道几种?
  6. 章国锋:视觉SLAM最新观点分享
  7. 7-1 堆栈操作合法性 (15 分)
  8. Linux BASH多进程并行处理的方法实现
  9. 百度图神经网络学习——day01
  10. 设置组策略的应用条件-----Windows 管理规范 (WMI)过虑器
  11. HTTP缓存原理及相关知识(1)
  12. 【jzoj5053】【石子游戏】【搜索】
  13. 个人学习笔记---Linux内存:内存管理的实质
  14. 关于网页背景图怎样自动适应屏幕大小
  15. 同一局域网下,手机能连上wifi,电脑连不上
  16. 布袋除尘器过滤风速多少_布袋除尘器的过滤风速一般取多少
  17. 罗克韦尔自动化通过收购ASEM加强控制和可视化产品组合
  18. 厦门大学“网宿杯“17届程序设计竞赛决赛(同步赛) #题解 #题目都超有趣呀
  19. envi查看灰度直方图_2.ENVI软件操作基础——查看数据属性特征
  20. 网络编程9_线程-条件,定时器,队列,线程池, 协程

热门文章

  1. 我的世界java营火如何合成_我的世界营火怎么合成
  2. 抽取JDBC工具类的方法
  3. 为保证消防设备在大型建筑正常运行,消防设备电源监控系统起着关键作用
  4. java毕业设计MVC的时鲜蔬菜配送系统Mybatis+系统+数据库+调试部署
  5. 使用fiddler实现手机抓包--关于苹果装了证书 不能上网的解决办法
  6. 深度解析|互金用户增长模型背后,最底层的逻辑框架
  7. 啊??解放?天性???
  8. 昨天又开始听到学校的铃声了。。。
  9. RNN之seq2seq模型
  10. python语句tiy_python-语句1