栏目分类:
子分类:
返回
终身学习网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
终身学习网 > IT > 软件开发 > 后端开发 > Java

ID生成器FireWork

Java 更新时间:发布时间: 百科书网 趣学号

最近写了个ID生成器: FireWork。项目地址: firework-id-generator

  • 16 byte顺序字符串[8byte时间戳 1byte回拨位 2byte ServiceId 5byte序列号]序列号不在下一秒重置
  • 总体趋势递增
  • 支持时间到 8888年左右
  • 支持3844台相同微服务之间id唯一
  • 支持时钟回拨无数次,61次后时间还小于上次回拨时间时通过消费未来时间确保ID不重复
  • 支持监听时钟回拨告警或者其他业务处理
  • 性能在多线程的时候表现良好(12000/ms 多余snowflake 4000/ms),理论上1s能生成1200wID
  • 通过实现存取接口来确保下次启动加载回拨位和回拨最大时间(可选)

业界关于ID生成器有比较多的解决方案

  1. 数据库自增
  2. UUID
  3. Snowflake
  4. Leaf
  5. MongDB ObjectId
  6. Uid-generator

但是无论哪个ID生成器,从设计上会考虑的问题可以归纳成几类:

  1. 无序/有序/趋势递增
  2. 有服务端/无服务端
  3. 是否依赖钟/是否依赖存储
  4. ID长度规划
  1. 无序/有序/趋势递增的抉择。

一般ID生成直接会影响数据库使用:

  1. 对于B+树的存储引擎,拿Innodb举例,每次插入都是更改Page页的数据,因为写入乱序,InnoDB不得不做频繁的页分列操作,为新的数据腾出空间。其次需要加载一下页到内存里就为了插入数据。

  2. 对于LSM Tree的存储引擎(比如Ocean base),虽然不影响插入时的性能,但是在做层级合并的时候,如果数据是随机的,会加载更多的文件,使写入放大。

综合起来我们会考虑趋势递增,因为有序递增在多线程上容易发生资源争抢。

  1. 有服务端/无服务端

1.有服务端的话,需要考虑每次网络请求的开销。基于这个基础上,我们一般会设计一个桶,每次拉取的时候拉取一个号段。这样就能减小开销。同时需要考虑服务端高可用,客户端需要缓存,在服务端无法使用的时候能继续消耗。

  1. 依赖时钟/依赖存储
  1. 不管是依赖时钟还是依赖存储,都是为了解决下次服务启动的时候,不会生成重复的ID。对于依赖存储的服务,有强依赖的比如依赖数据库生成号段的。只依赖时钟只能解决运行时的时钟回拨(可以用来消费未来时间),但是无法保障服务重启以后,再出现时钟回拨。
  1. 长度规划

    1. 类似Snowflake 8 byte的话支持不超过100年
    2. 但是如果不用8 byte的话,就需要考虑String了

总体考虑来说:

选了趋势递增,无服务端,依赖时钟,弱依赖存储(可选),长度16byte。

权衡带来的好处也有:

  1. 插入性能比较好。(按照字符串ascii码趋势递增)
  2. 使用简单,不用搭建服务端。
  3. 解决时钟回拨的问题
    1. 通过回拨位每次时钟回拨修改回拨位的值,并且记录上次回拨时间。
    2. 如果修改的回拨位已经有回拨记录,并且当前时间少于它,就算出一个差值,来消费未来时间。
    3. 解决snowflake时钟回拨检测加锁。在多线程下性能是snowflake3倍以上。
  4. 支持时间到 8888年左右

使用也比较简单

简单使用

添加MAVEN 依赖


  io.gitee.binaryfox
  firework-id-generator
  1.0

使用

  FireWorkGenerator.init(0, null);
  System.out.println(FireWorkGenerator.nextId());
高阶使用
  
        


        FireWorkGenerator.init(0, new FireWorkStepBackHandler() {
            @Override
            public void notifyStepBack(long[] before) {
                
                //比如回拨到63的一半水位线就告警
                int count = 0;
                for (long l : before) {
                    if (l != 0) {
                        count++;
                    }
                    if (count > before.length / 2) {
                        //输出error日志 并且监控报警
                    }
                }

            }

            @Override
            public long[] getStepBackTimeRecordArray(int serviceId) {
                
                //比如 application_name+serviceId当作key 取一个list
                return null;
            }

            @Override
            public void setStepBackTimeRecordArray(int serviceId, int index, long timeBeforeStepBack) {
                
                //比如 application_name+serviceId当作key 存一个list
            }

            @Override
            public void setStep(int serviceId, long step) {
                
                //比如 application_name+serviceId当作key 存一个long
            }

            @Override
            public long getStep(int serviceId) {
                
                //比如 application_name+serviceId当作key 存一个long
                return 0;
            }
        });
        //使用
        String s = FireWorkGenerator.nextId();
转载请注明:文章转载自 www.051e.com
本文地址:http://www.051e.com/it/273650.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 ©2023-2025 051e.com

ICP备案号:京ICP备12030808号