分布式ID服务实现全面解析

news2025/3/31 17:23:18

分布式ID生成器是分布式系统中的关键基础设施,用于在分布式环境下生成全局唯一的标识符。以下是各种实现方案的深度解析和最佳实践。

一、核心需求与设计考量

1. 核心需求矩阵

需求

重要性

实现难点

全局唯一

必须保证

时钟回拨/节点冲突

高性能

高并发场景关键

锁竞争/网络开销

有序性

分页查询友好

时间戳精度问题

高可用

服务不可中断

故障转移/数据恢复

易用性

接入成本低

协议兼容性

2. 典型业务场景

  • 电商订单号生成
  • 金融交易流水号
  • 物联网设备标识
  • 分布式日志追踪
  • 数据库分片键

二、主流实现方案对比

1. 方案全景图

mermaid

graph TD
    A[分布式ID] --> B[中心化]
    A --> C[去中心化]
    B --> D[数据库序列]
    B --> E[Redis原子操作]
    B --> F[Zookeeper节点]
    C --> G[UUID]
    C --> H[Snowflake]
    C --> I[Leaf/美团]

2. 详细方案对比

方案

示例ID

优点

缺点

QPS

UUID

550e8400-e29b-41d4-a716-446655440000

无中心节点

无序存储效率低

100,000+

数据库自增

1234567

简单可靠

单点瓶颈

1,000~5,000

Redis INCR

INCR global:id

性能较好

持久化问题

50,000~100,000

Snowflake

1256897123456789504

有序紧凑

时钟敏感

100,000+

Leaf-Segment

ID: 12345 (biz_tag:order)

缓冲优化

需DB配合

50,000+

Tinyid

[10001,10010]

批量获取

强依赖ZK

20,000+

三、Snowflake 深度实现

1. 标准位分配

0 - 0000000000 0000000000 0000000000 0000000000 0 - 00000 - 00000 - 000000000000
|____________________________| |________| |_____| |________________________|
        时间戳(41bit)          数据中心(5bit) 机器ID(5bit) 序列号(12bit)

2. Java优化实现

public class SnowflakeIdGenerator {
    private final long twepoch = 1288834974657L; // 起始时间戳(2010-11-04)
    private final long workerIdBits = 5L;
    private final long datacenterIdBits = 5L;
    private final long sequenceBits = 12L;
    
    private final long maxWorkerId = -1L ^ (-1L << workerIdBits);
    private final long maxDatacenterId = -1L ^ (-1L << datacenterIdBits);
    
    private volatile long lastTimestamp = -1L;
    private volatile long sequence = 0L;

    public synchronized long nextId() {
        long timestamp = timeGen();
        
        // 时钟回拨处理
        if (timestamp < lastTimestamp) {
            throw new RuntimeException("Clock moved backwards");
        }
        
        if (lastTimestamp == timestamp) {
            sequence = (sequence + 1) & ((1 << sequenceBits) - 1);
            if (sequence == 0) {
                timestamp = tilNextMillis(lastTimestamp);
            }
        } else {
            sequence = 0;
        }
        
        lastTimestamp = timestamp;
        
        return ((timestamp - twepoch) << (workerIdBits + sequenceBits)) 
               | (datacenterId << (workerIdBits + sequenceBits))
               | (workerId << sequenceBits) 
               | sequence;
    }
    
    protected long tilNextMillis(long lastTimestamp) {
        // 阻塞到下一毫秒
        long timestamp;
        do {
            timestamp = timeGen();
        } while (timestamp <= lastTimestamp);
        return timestamp;
    }
}

3. 时钟回拨解决方案

方案

实现方式

适用场景

异常抛出

直接拒绝请求

严格要求时序

等待时钟

自旋直到时钟追回

短暂回拨(<100ms)

备用ID池

提前生成备用ID

容忍短暂无序

扩展位记录

增加回拨计数位

需要改造ID结构

四、Leaf-Segment 方案详解

1. 数据库设计

CREATE TABLE `leaf_alloc` (
  `biz_tag` varchar(128) NOT NULL,
  `max_id` bigint NOT NULL DEFAULT '1',
  `step` int NOT NULL,
  `update_time` timestamp NOT NULL,
  PRIMARY KEY (`biz_tag`)
) ENGINE=InnoDB;

2. 双Buffer优化流程

sequenceDiagram
    participant Client
    participant Service
    participant DB
    
    Client->>Service: 获取ID(biz_tag=order)
    Service->>DB: 查询当前max_id和step
    DB-->>Service: max_id=1000, step=1000
    Service->>Service: 分配本地缓存[1001-2000]
    Service->>Client: 返回1001
    Service->>DB: 异步更新max_id=2000
    Note right of Service: Buffer1耗尽前<br>提前加载Buffer2

3. 异常处理机制

  • DB故障:使用本地缓存直到耗尽
  • ID耗尽:动态调整step大小
  • 双Buffer同时失效:降级到同步获取

五、高性能服务架构

1. 服务化部署架构

+-----------------+
                   |  Load Balancer  |
                   +--------+--------+
                            |
           +----------------+----------------+
           |                |                |
    +------+------+  +------+------+  +------+------+
    |  ID Service |  |  ID Service |  |  ID Service |
    +------+------+  +------+------+  +------+------+
           |                |                |
    +------+------+  +------+------+  +------+------+
    |   Redis     |  |    DB      |  |  Zookeeper  |
    +-------------+  +------------+  +-------------+

2. 性能优化技巧

技术

效果

实现示例

本地缓存

减少网络IO

ConcurrentHashMap

存储Segment

批量获取

降低DB压力

UPDATE SET max_id=max_id+1000

异步持久化

提高吞吐

先响应后写WAL日志

分层设计

故障隔离

内存->Redis->DB 三级获取

3. 容灾方案对比

方案

恢复时间

数据丢失风险

主从同步

秒级

少量异步数据

多活部署

几乎为零

定期快照

分钟级

取决于备份频率

六、生产实践案例

案例1:电商订单ID

需求

  • 每日亿级订单
  • 需要时间有序
  • 包含业务类型信息

实现

// 格式: 业务类型(2位) + 时间(yyMMddHHmm) + 序列(6位) + 机器(3位)
public String generateOrderId(String bizType) {
    String timePart = new SimpleDateFormat("yyMMddHHmm").format(new Date());
    long seq = redis.incr("order:id:" + timePart);
    return String.format("%s%s%06d%03d", 
        bizType, timePart, seq % 1000000, machineId);
}

import java.util.Queue;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;

@Service
public class BufferedOrderIdGenerator {

    private final Queue<String> idPool = new ConcurrentLinkedQueue<>();
    private final RedisOrderIdGenerator redisGenerator;
    private final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
    private static final int BATCH_SIZE = 100;
    private static final int REFILL_THRESHOLD = 20;

    public BufferedOrderIdGenerator(RedisOrderIdGenerator redisGenerator) {
        this.redisGenerator = redisGenerator;
        this.scheduler.scheduleAtFixedRate(this::refillPool, 0, 1, TimeUnit.SECONDS);
    }

    public String getOrderId() {
        String id = idPool.poll();
        if (id == null) {
            // 缓冲池为空时同步获取
            return redisGenerator.generateOrderId();
        }
        return id;
    }

    private void refillPool() {
        if (idPool.size() < REFILL_THRESHOLD) {
            // 批量预生成ID
            String date = LocalDate.now().format(DATE_FORMAT);
            Long startSeq = redisTemplate.opsForValue()
                .increment(ORDER_ID_PREFIX + date, BATCH_SIZE);
                
            for (long i = startSeq - BATCH_SIZE + 1; i <= startSeq; i++) {
                idPool.add(String.format(ID_FORMAT, date, i));
            }
            
            redisTemplate.expire(ORDER_ID_PREFIX + date, 48, TimeUnit.HOURS);
        }
    }
}


import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.concurrent.TimeUnit;

@Service
public class RedisOrderIdGenerator {

    private final StringRedisTemplate redisTemplate;
    private static final String ORDER_ID_PREFIX = "order:id:";
    private static final DateTimeFormatter DATE_FORMAT = DateTimeFormatter.BASIC_ISO_DATE;
    
    // 订单ID格式:年月日(8位) + 序列号(8位)
    private static final String ID_FORMAT = "%s%08d"; 

    public RedisOrderIdGenerator(StringRedisTemplate redisTemplate) {
        this.redisTemplate = redisTemplate;
    }

    /**
     * 生成订单ID
     */
    public String generateOrderId() {
        String date = LocalDate.now().format(DATE_FORMAT);
        String key = ORDER_ID_PREFIX + date;
        
        // 使用Redis原子操作INCR
        Long sequence = redisTemplate.opsForValue().increment(key);
        
        // 设置48小时过期(避免跨日期问题)
        redisTemplate.expire(key, 48, TimeUnit.HOURS);
        
        return String.format(ID_FORMAT, date, sequence);
    }
}
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.transaction.annotation.Transactional;

@Service
public class PersistentOrderIdService {

    private final RedisOrderIdGenerator redisGenerator;
    private final JdbcTemplate jdbcTemplate;
    
    // WAL(Write-Ahead Log)表
    private static final String WAL_TABLE = "order_id_wal";
    
    public PersistentOrderIdService(RedisOrderIdGenerator redisGenerator, 
                                  JdbcTemplate jdbcTemplate) {
        this.redisGenerator = redisGenerator;
        this.jdbcTemplate = jdbcTemplate;
    }

    /**
     * 带持久化保障的ID生成
     */
    @Transactional
    public String generatePersistentOrderId() {
        // 1. 先写入预写日志
        String pendingId = redisGenerator.generateOrderId();
        jdbcTemplate.update(
            "INSERT INTO " + WAL_TABLE + " (order_id, create_time, status) VALUES (?, NOW(), 'PENDING')",
            pendingId);
        
        // 2. 确认写入Redis
        // 如果Redis操作失败会抛出异常触发事务回滚
        
        // 3. 更新日志状态
        jdbcTemplate.update(
            "UPDATE " + WAL_TABLE + " SET status = 'CONFIRMED' WHERE order_id = ?",
            pendingId);
            
        return pendingId;
    }
    
    /**
     * 恢复未确认的ID
     */
    @Scheduled(fixedRate = 60000) // 每分钟执行一次
    public void recoverPendingIds() {
        jdbcTemplate.query(
            "SELECT order_id FROM " + WAL_TABLE + " WHERE status = 'PENDING' AND create_time > DATE_SUB(NOW(), INTERVAL 1 HOUR)",
            (rs, rowNum) -> rs.getString("order_id"))
            .forEach(pendingId -> {
                if (!redisTemplate.hasKey(buildRedisKey(pendingId))) {
                    redisTemplate.opsForValue().set(buildRedisKey(pendingId), 
                        extractSequence(pendingId));
                }
            });
    }
    
    private String buildRedisKey(String orderId) {
        return ORDER_ID_PREFIX + orderId.substring(0, 8); // 提取日期部分
    }
    
    private long extractSequence(String orderId) {
        return Long.parseLong(orderId.substring(8));
    }
}

案例2:分布式追踪ID

需求

  • 全局唯一
  • 高吞吐
  • 可解析

实现(借鉴Twitter的Zipkin)

// 128-bit ID = 应用节点(32bit) + 时间(64bit) + 随机数(32bit)
public static String newTraceId() {
    return String.format("%08x%016x%08x",
        nodeId,
        System.currentTimeMillis(),
        ThreadLocalRandom.current().nextInt());
}

七、监控与治理

1. 关键监控指标

指标

采集方式

告警阈值

ID生成延迟

Micrometer Timer

P99 > 10ms

段缓存命中率

缓存统计

<90%

时钟偏移量

NTP监控

>50ms

DB连接池使用率

Druid监控

>80%

2. 运维指令集

bash

# 动态调整Leaf步长
curl -X POST "http://id-service/segment/step?bizTag=order&step=5000"

# 强制刷新缓存
redis-cli DEL leaf:order:cache

# 节点下线
./admin.sh disableNode --nodeId=3

八、选型决策树

mermaid

graph TD
    A[是否需要有序ID?] -->|是| B[考虑Snowflake/Leaf]
    A -->|否| C[考虑UUID]
    B --> D[QPS>10万?]
    D -->|是| E[Leaf-Segment+缓存]
    D -->|否| F[原生Snowflake]
    C --> G[需要可读性?]
    G -->|是| H[时间戳+序列组合]
    G -->|否| I[标准UUIDv4]

通过深入理解这些实现方案和架构设计,可以构建出满足不同业务场景需求的分布式ID服务。建议根据实际业务规模、性能要求和运维能力进行技术选型。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2324323.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

dom0运行android_kernel: do_serror of panic----failed to stop secondary CPUs 0

问题描述&#xff1a; 从日志看出,dom0运行android_kernel&#xff0c;刚开始运行就会crash,引发panic 解决及其原因分析&#xff1a; 最终问题得到解决&#xff0c;发现是前期在调试汇编阶段代码时&#xff0c;增加了汇编打印的指令&#xff0c;注释掉这些指令,问题得到解决。…

HarmonyOS NEXT——【鸿蒙原生应用加载Web页面】

鸿蒙客户端加载Web页面&#xff1a; 在鸿蒙原生应用中&#xff0c;我们需要使用前端页面做混合开发&#xff0c;方法之一是使用Web组件直接加载前端页面&#xff0c;其中WebView提供了一系列相关的方法适配鸿蒙原生与web之间的使用。 效果 web页面展示&#xff1a; Column()…

优选算法的慧根之翼:位运算专题

专栏&#xff1a;算法的魔法世界 个人主页&#xff1a;手握风云 一、位运算 基础位运算 共包含6种&(按位与&#xff0c;有0就是0)、|(按位或有1就是1)、^(按位异或&#xff0c;相同为0&#xff0c;相异为1)、~(按位取反&#xff0c;0变成1&#xff0c;1变成0)、<<(左…

图论问题集合

图论问题集合 寻找特殊有向图&#xff08;一个节点最多有一个出边&#xff09;中最大环路问题特殊有向图解析算法解析步骤 1 &#xff1a;举例分析如何在一个连通块中找到环并使用时间戳计算大小步骤 2 &#xff1a;抽象成算法注意 实现 寻找特殊有向图&#xff08;一个节点最多…

【数据结构】栈 与【LeetCode】20.有效的括号详解

目录 一、栈1、栈的概念及结构2、栈的实现3、初始化栈和销毁栈4、打印栈的数据5、入栈操作---栈顶6、出栈---栈顶6.1栈是否为空6.2出栈---栈顶 7、取栈顶元素8、获取栈中有效的元素个数 二、栈的相关练习1、练习2、AC代码 个人主页&#xff0c;点这里~ 数据结构专栏&#xff0c…

Redis设计与实现-哨兵

哨兵模式 1、启动并初始化sentinel1.1 初始化服务器1.2 使用Sentinel代码1.3 初始化sentinel状态1.4 初始化sentinel状态的master属性1.5 创建连向主服务器的网络连接 2、获取主服务器信息3、获取从服务器的信息4、向主从服务器发送信息5、接受主从服务器的频道信息6、检测主观…

C++进阶——封装哈希表实现unordered_map/set

与红黑树封装map/set基本相似&#xff0c;只是unordered_map/set是单向迭代器&#xff0c;模板多传一个HashFunc。 目录 1、源码及框架分析 2、模拟实现unordered_map/set 2.1 复用的哈希表框架及Insert 2.2 iterator的实现 2.2.1 iteartor的核心源码 2.2.2 iterator的实…

【算法day25】 最长有效括号——给你一个只包含 ‘(‘ 和 ‘)‘ 的字符串,找出最长有效(格式正确且连续)括号子串的长度。

32. 最长有效括号 给你一个只包含 ‘(’ 和 ‘)’ 的字符串&#xff0c;找出最长有效&#xff08;格式正确且连续&#xff09;括号子串的长度。 https://leetcode.cn/problems/longest-valid-parentheses/ 2.方法二&#xff1a;栈 class Solution { public:int longestValid…

Jenkins + CICD流程一键自动部署Vue前端项目(保姆级)

git仓库地址&#xff1a;参考以下代码完成,或者采用自己的代码。 南泽/cicd-test 拉取项目代码到本地 使用云服务器或虚拟机采用docker部署jenkins 安装docker过程省略 采用docker部署jenkins&#xff0c;注意这里的命令&#xff0c;一定要映射docker路径&#xff0c;否则无…

一款超级好用且开源免费的数据可视化工具——Superset

认识Superset 数字经济、数字化转型、大数据等等依旧是如今火热的领域&#xff0c;数据工作有一个重要的环节就是数据可视化。 看得见的数据才更有价值&#xff01; 现如今依旧有多数企业号称有多少多少数据&#xff0c;然而如果这些数据只是呆在冷冰冰的数据库或文件内则毫无…

RedHatLinux(2025.3.22)

1、创建/www目录&#xff0c;在/www目录下新建name和https目录&#xff0c;在name和https目录下分别创建一个index.htm1文件&#xff0c;name下面的index.html 文件中包含当前主机的主机名&#xff0c;https目录下的index.htm1文件中包含当前主机的ip地址。 &#xff08;1&…

【C++篇】类与对象(上篇):从面向过程到面向对象的跨越

&#x1f4ac; 欢迎讨论&#xff1a;在阅读过程中有任何疑问&#xff0c;欢迎在评论区留言&#xff0c;我们一起交流学习&#xff01; &#x1f44d; 点赞、收藏与分享&#xff1a;如果你觉得这篇文章对你有帮助&#xff0c;记得点赞、收藏&#xff0c;并分享给更多对C感兴趣的…

智慧运维平台:赋能未来,开启高效运维新时代

在当今数字化浪潮下&#xff0c;企业IT基础设施、工业设备及智慧城市系统的复杂度与日俱增&#xff0c;传统人工运维方式已难以满足高效、精准、智能的管理需求。停机故障、低效响应、数据孤岛等问题直接影响企业运营效率和成本控制。大型智慧运维平台&#xff08;AIOps, Smart…

基于大语言模型的智能音乐创作系统——从推荐到生成

一、引言&#xff1a;当AI成为音乐创作伙伴 2023年&#xff0c;一款由大语言模型&#xff08;LLM&#xff09;生成的钢琴曲《量子交响曲》在Spotify冲上热搜&#xff0c;引发音乐界震动。传统音乐创作需要数年专业训练&#xff0c;而现代AI技术正在打破这一壁垒。本文提出一种…

Reactive编程:什么是Reactive编程?Reactive编程思想

文章目录 **1. Reactive编程概述****1.1 什么是Reactive编程&#xff1f;****1.1.1 Reactive编程的定义****1.1.2 Reactive编程的历史****1.1.3 Reactive编程的应用场景****1.1.4 Reactive编程的优势** **1.2 Reactive编程的核心思想****1.2.1 响应式&#xff08;Reactive&…

深度剖析:U盘突然无法访问的数据拯救之道

一、引言 在数字化办公与数据存储日益普及的当下&#xff0c;U盘凭借其小巧便携、即插即用的特性&#xff0c;成为了人们工作、学习和生活中不可或缺的数据存储工具。然而&#xff0c;U盘突然无法访问这一棘手问题却时常困扰着广大用户&#xff0c;它不仅可能导致重要数据的丢失…

蓝桥杯-特殊的三角形(dfs/枚举/前缀和)

思路分析 深度优先搜索&#xff08;DFS&#xff09;思路 定义与参数说明 dfs 函数中&#xff0c;last 记录上一条边的长度&#xff0c;用于保证新选边长度大于上一条边&#xff0c;实现三边互不相等 。cnt 记录已选边的数量&#xff0c;当 cnt 达到 3 时&#xff0c;就构成了…

一文详解k8s体系架构知识

0.云原生 1.k8s概念 1. k8s集群的两种管理角色 Master&#xff1a;集群控制节点&#xff0c;负责具体命令的执行过程。master节点通常会占用一股独立的服务器&#xff08;高可用部署建议用3台服务器&#xff09;&#xff0c;是整个集群的首脑。 Master节点一组关键进程&#xf…

wx162基于springboot+vue+uniapp的在线办公小程序

开发语言&#xff1a;Java框架&#xff1a;springbootuniappJDK版本&#xff1a;JDK1.8服务器&#xff1a;tomcat7数据库&#xff1a;mysql 5.7&#xff08;一定要5.7版本&#xff09;数据库工具&#xff1a;Navicat11开发软件&#xff1a;eclipse/myeclipse/ideaMaven包&#…

Baklib内容中台的核心优势是什么?

智能化知识管理引擎 Baklib的智能化知识管理引擎通过多源数据整合与智能分类技术&#xff0c;实现企业知识资产的自动化归集与动态更新。系统内置的语义分析算法可自动识别文档主题&#xff0c;结合自然语言处理技术生成结构化标签体系&#xff0c;大幅降低人工标注成本。针对…