一个改进的 Java 短 ID 生成库 - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
爱意满满的作品展示区。
tlanyan
V2EX    分享创造

一个改进的 Java 短 ID 生成库

  •  
  •   tlanyan 54 天前 1693 次点击
    这是一个创建于 54 天前的主题,其中的信息可能已经有所发展或是发生改变。

    目前知名且好用的短整数 ID 生成器是 Yitter 的 IdGenerator。相对于其他雪花类算法库,IdGenerator 最大的优点就是不固定长度,可按需配置各个段的值或者偏移,大幅减少了 ID 的长度,避免了号段浪费。如果并发量上来了,可以直接更改配置实现无缝升级。

    IdGenerator 改进明显,但是对于本人手上的小项目来说 ID 还是太长了(默认配置下随手甩出来的 ID 就是上万亿的数),也用不着这么高的并发(别说一秒 5W 个 id ,一天能有 5W 个订单都能让人笑醒)。此外全局只能一个生成器,不能根据业务场景采用不同的生成器:购买订单和退货订单 ID 是可以重复的,并且一般情况下退货订单数量比购买订单少一个量级,可以采用更短的 ID 。

    出于上面的考虑,本人对 IdGenerator 库进行了如下改进:

    1. 支持不同的时间精度。原版中默认是 1 毫秒,现在新增了 10 毫秒 10 秒(实际上是 8 毫秒、128 毫秒、1024 毫秒 和 8192 毫秒,方便用位运算代替整数除法)的精度选项。低精度选项(例如 1 秒、10 秒)适合并发量小的项目(例如 1 秒内最多 30 个订单),能大大减少 ID 的长度;

    2. WorkIdBitLength 支持为 0 ,让单机应用能生成更短或数值更小的 ID ;

    3. 支持不同场景使用不同的 Id 生成器,同时兼容原来的接口:

    // 兼容原版使用方式 var optiOns= new IdGeneratorOptions(); YitIdHelper.setIdGenerator(options); var newId = YitIdHelper.nextId(); // 退款订单场景 final var REFUND = "refund"; var options2 = new IdGeneratorOptions(); // 退款相当于购买订单频率更低,可以降低时间精度要求 options2.Precision=1; YitIdHelper.setIdGenerator(REFUND, options2); var newId = YitIdHelper.nextId(REFUND); 本人测试了不同算法和时间精度下生成的 ID ,结果为: # 调用 Hutool 的 IdUtil.getSnowflakeNextId() 方法生成 这是用方法 snowflake 生成的 Id: 1957067304089067520 (长度 19 位) ===================================== # 使用 IdGenerator 的默认配置,无任何改动 这是用方法 默认 生成的 Id: 80881995821061 (长度 14 位) ===================================== # 修改 options.Precision = 1 这是用方法 precision1 生成的 Id:10110249734149 (长度 14 位) ===================================== # 修改 options.Precision = 2 这是用方法 precision2 生成的 Id:631890624517 (长度 12 位) ===================================== # 修改 options.Precision = 3 这是用方法 precision3 生成的 Id:78986326021 (长度 11 位) ===================================== # 修改 options.Precision = 4 这是用方法 precision4 生成的 Id:9873293317 (长度 10 位) 

    对于本人手上的小项目,没有多大的并发( 1 天大概几百个订单),可以配置生成更短的 ID:

    var optiOns= new IdGeneratorOptions(); options.Precision = 4; // 时间精度为 10 秒(实际上是 8 秒) options.WorkerIdBitLength = 1; // 最多两个应用实例 options.SeqBitLength = 6; // 每 10 秒可以生成 29 个订单,高峰期超过也没关系,会自动进行时间漂移 YitIdHelper.setIdGenerator("order", options); var id = YitIdHelper.nextId("order"); # 生成的 ID:2707882245 

    此配置下订单号为 10 位,这个长度是非常能接受的,并且值也很小。

    Java 版本的改进代码已经放到 Github ,其他语言版本本人暂时用不着,因为未更改。如果有需要按照 Java 版本的方式更改即可,代码量其实很少。另外目前没有 Maven 包,使用时需要将代码下载下来直接使用。

    10 条回复    2025-08-24 20:44:06 +08:00
    maokg
        1
    maokg  
       54 天前
    改进->改退(开玩笑,适合自己场景的库才是最好的
    xhawk
        2
    xhawk  
       54 天前 via Android
    前几天看了一下,如果订单是跟数据库做一个绑定的话,那这个订单号其实逻辑上就可以设计的很短。意思就是说,我们先做一个简单的假设,一个数据库可以存放 1 亿的订单,经过这样子分库之后,那么这个订单就可以无限地变大了
    tlanyan
        3
    tlanyan  
    OP
       54 天前
    @maokg 确实可以认为是并发量下降了
    tlanyan
        4
    tlanyan  
    OP
       54 天前
    @xhawk 远不到分库的时候,十多天才 1 万单
    netnr
        5
    netnr  
       54 天前
    一直在用 53 位的雪花 ID ,兼容前端 JS Number.MAX_SAFE_INTEGER ,单机版

    Timestamp 41 位的时间戳,约 69 年
    Sequence 12 位的序列号,4096/ms
    tlanyan
        6
    tlanyan  
    OP
       54 天前
    @netnr 只是不想要那么长又没什么意义的 ID ,能用多少年倒是其次,另外完全用不到这么高的并发
    clarkethan
        7
    clarkethan  
       53 天前
    追求很短的 id ,建议全局递增即可

    另外,同一个系统内的购买订单和退货订单,如果可能,id 还是尽量别重复为好,毕竟 id 是一个还算充足的的资源
    tlanyan
        8
    tlanyan  
    OP
       53 天前
    @clarkethan 就是不想要递增 id 才用这类 id 算法,另外这里说的可以重复只是一个避免心智负担的要求,这类 id 生成算法基本上就不太可能生成重复 id
    LoNeZ
        9
    LoNeZ  
       51 天前
    ...不都是 4 字节 8 字节吗...为什么会嫌长?
    tlanyan
        10
    tlanyan  
    OP
       48 天前
    8 个字节字符串最大将近 20 位,那是非常长
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     885 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 32ms UTC 19:44 PVG 03:44 LAX 12:44 FK 15:44
    Do have faith in what you're doing.
    ubao snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86