唯一ID全局有序或递增

wen java案例 1

本文目录导读:

唯一ID全局有序或递增

  1. 等级一:全局唯一,且趋势递增
  2. 等级二:全局唯一,且严格单调递增
  3. 等级三:强要求有序(如排序、流水号)
  4. 总结与推荐

这是一个非常经典的分布式系统设计问题,首先需要明确:“全局唯一”和“全局单调递增”是两个不同的目标,且在分布式系统中往往需要权衡。

根据你的需求,我将其分为三个等级,并给出对应的生成策略。

全局唯一,且趋势递增

这是最常见的需求(如数据库主键、订单号等),不需要严格递增(即下一秒的ID必须大于上一秒),只要整体呈上升趋势,便于数据库B+树索引维护即可。

雪花算法及其变体

  • 原理:64位Long型整数。

    1位符号位 + 41位时间戳 + 10位工作机器ID + 12位序列号。

  • 特点
    • 唯一:通过数据中心ID + 机器ID + 毫秒内自增序列保证。
    • 趋势递增:时间戳在高位,所以整体趋势递增。
    • 无状态:服务本地生成,无网络IO。
  • 缺点:依赖机器时钟,时钟回拨会导致ID重复或乱序。
  • 变体
    • 百度的uid-generator:将机器ID替换为自增ID,减弱时钟依赖。
    • 美团的Leaf:优化了时钟回拨问题,提供snowflake和segment两种模式。

数据库号段模式(Segment)

  • 原理:集中式数据库分发批次。
    • 在数据库表中维护一个max_id字段。
    • 应用每次从数据库申请一段ID(如[1000, 2000))。
    • 应用本地内存中生成该段内的ID。
  • 特点
    • 有序:按申请顺序严格递增。
    • 高可用:本地生成,减少数据库压力。
    • 易于回滚:即使应用崩溃,已分配的ID段不会浪费太多。
  • 缺点:依赖中央数据库(如MySQL)的可用性。

全局唯一,且严格单调递增

注意: 在分布式场景下,严格单调递增(即每个ID比前一个ID大1)几乎无法在无锁、高性能的情况下实现,它强制所有生成节点必须串行化。

  • 核心方案单点串行化
    • 使用一个Redis INCR 命令、或一个数据库的自增ID。
    • 代价:完全失去并发性能,成为全局瓶颈。

除非你的并发量极低(< 100 QPS),否则不要追求严格单调递增。

强要求有序(如排序、流水号)

如果需要生成的ID具备某种业务语义(如日期 + 序号),可以用以下方式组合:

  1. 日期前缀 + 自增后缀

    • 格式:YYYYMMDD + 固定位数序列号
    • 实现:Redis每日一个Key(INCR order:20231027),或数据库每日一个计数器。
    • 缺点:高并发下需注意Redis的单点瓶颈。
  2. Lua脚本保证原子性

    在Redis中执行Lua脚本,同时生成时间和序列,避免并发冲突。

总结与推荐

需求等级 推荐方案 优劣势
唯一 + 趋势递增 雪花算法(Snowflake) 高效,无网络依赖,适合常规分布式场景。
唯一 + 趋势递增 + 强可用 美团Leaf / 百度uid-generator 解决了雪花算法的时钟回拨问题。
唯一 + 严格递增(低并发) 数据库自增ID / Redis INCR 简单,但性能受限。
高并发 + 业务有序 日期前缀 + Redis自增 适合有业务含义的流水号。

一句话建议:

  • 99%的场景用雪花算法(或它的成熟变体)。
  • 如果无法容忍ID位数过长,改用数据库号段模式
  • 只有当你确定需要“下一个ID比上一个正好大1”且并发小于几千时,才考虑单点串行化方案。

抱歉,评论功能暂时关闭!