当前位置: 首页 > news >正文

分布式ID

文章目录

    • 1. 数据库自增ID
    • 2. UUID
    • 4. 数据库号段模式
    • 5. Redis分布式ID
    • 6.百度的uid-generator
    • 7. 基于Zookeeper的顺序节点
    • 8. 数据库集群模式
    • 9. 美团(Leaf)
    • 10. 滴滴(Tinyid)

在分布式系统中,生成全局唯一的ID是一个常见需求,通常可以采用以下几种方案来实现分布式ID生成:

  1. UUID(Universally Unique Identifier):
    UUID是一种标准的128位唯一标识符,通常以32个十六进制数字表示。UUID可以在不同节点上生成,保证全局唯一性,但由于长度较长且无序,不适合作为数据库主键使用。
  2. Snowflake算法:
    Snowflake是Twitter开源的分布式ID生成算法,通过对64位的ID进行分段组合生成唯一ID。其中包括时间戳、机器标识和序列号等部分,保证了生成的ID在分布式环境下的唯一性和有序性。
  3. Flake ID生成算法:
    Flake是另一种分布式ID生成算法,类似于Snowflake算法,通过时间戳、机器标识符和序列号来生成唯一ID。Flake算法相对于Snowflake算法有一些改进,例如更灵活的位数分配等。
  4. 数据库自增ID:
    在分布式环境中也可以使用数据库的自增ID作为全局唯一ID,但需要考虑数据库的性能和单点故障问题。
  5. 基于Redis或ZooKeeper的分布式锁生成ID:
    可以通过获取分布式锁的方式来保证ID的唯一性,例如利用Redis或ZooKeeper实现分布式锁,然后生成唯一ID。
  6. 利用分布式缓存生成ID:
    可以利用分布式缓存如Redis等来保存自增的ID,每次需要生成ID时从缓存中获取并递增,确保唯一性。
    以上是一些常见的分布式ID生成方案,选择合适的方案需要根据具体业务场景、性能需求和可用性要求来进行评估和选择。

数据库自增长序列或字段
UUID
Redis 生成 ID
基于雪花算法(Snowflake)实现
利用 zookeeper 生成唯一 ID
MongoDB 的 ObjectId
百度 (Uidgenerator)
美团(Leaf)

  1. uuid,它保证对在同一时空中所有机器都唯一,但这种方式生成id 比较长,并且无序,插入浪费空间。
  2. Snowflake 雪花算法,这种方案不错,但如果某台机器系统时钟回拨,有可能造成 ID 冲突重复,或者 ID 乱序(考虑跨机房部署问题)
  3. Mysql 自增主键,在高并发下,db 写压力会很大
  4. 用 Redis 做自增 id 生成器,性能高,但要考虑持久性问题;或者改造雪花算法,通过改造 workId 解决时钟回拨问题)

我们日常开发中,经常需要使用到分布式ID。我们系统一般都是分布式部署的,一些分布式锁、幂等、数据库的唯一键,都需要分布式ID。

1. 数据库自增ID

原理:利用数据库自增字段(如MySQL的AUTO_INCREMENT)生成唯一ID

优点:简单易用、ID有序、索引效率高缺点:单点故障、扩展性差(分库分表困难)
适用场景:单机或简单主从架构系统
代码示例:
CREATE TABLE orders (
id INT AUTO_INCREMENT PRIMARY KEY,
order_data VARCHAR(255)
);

2. UUID

● 原理:基于MAC地址、时间戳、随机数生成128位字符串
● 优点:全局唯一、无需中心化服务
● 缺点:无序导致索引效率低、存储空间大(36字符)
● 适用场景:日志跟踪、非核心业务(如临时会话)
我们的项目中,有些伙伴为了简单方便,有时候会直接用它,如果业务性比较强的,就在它后缀拼接写个性化标记(业务标记)进来~
代码示例:
import java.util.UUID;
String uuid = UUID.randomUUID().toString();
3. 雪花算法(Snowflake)
● 原理:64位结构 = 时间戳(41位) + 机器ID(10位) + 序列号(12位)

● 优点:高性能(单机每秒4万+)、趋势递增29
● 缺点:依赖时钟同步(时钟回拨会导致重复)
● 适用场景:分布式高并发系统(如电商订单)
其实,我们现在的系统,很多场景就是用雪花算法生成的,如流水号等等~
代码示例:

public class Snowflake {private long machineId;private long sequence = 0L;private long lastTimestamp = -1L;public synchronized long nextId() {long timestamp = System.currentTimeMillis();if (timestamp < lastTimestamp) {throw new RuntimeException("时钟回拨!");}if (timestamp == lastTimestamp) {sequence = (sequence + 1) & 4095; // 12位序列号if (sequence == 0) timestamp = tilNextMillis(lastTimestamp);} else {sequence = 0L;}lastTimestamp = timestamp;return (timestamp << 22) | (machineId << 12) | sequence;}
}

4. 数据库号段模式

● 原理:批量获取ID段(如一次取1000个),减少数据库访问
● 优点:降低数据库压力、可用性高(缓存号段)、速度快
● 缺点:在服务器重启或故障转移等情况下,可能会导致ID的生成出现不连续的情况。
● 适用场景:中等并发业务(如用户ID生成)
我们的一些客户号,当前是用号段模式生成的,然后拼一些业务标记
表结构:

CREATE TABLE id_segment (biz_tag VARCHAR(50) PRIMARY KEY,max_id BIGINT NOT NULL,step INT NOT NULL,version INT NOT NULL
);

5. Redis分布式ID

● 原理:利用INCR原子操作生成递增ID

● 优点:性能优于数据库、天然有序、高性能、可扩展性强
● 缺点:依赖Redis可用性
● 适用场景:按日生成的流水号(如订单号=日期+自增)
代码示例:
Jedis jedis = new Jedis(“redis-host”);
Long orderId = jedis.incr(“order:20240526”);

6.百度的uid-generator

优点:避免频繁生成、吞吐量提升至600万/秒
适用场景:超大规模分布式系统
基于Twitter的Snowflake算法进行改进,增加了更多的配置和灵活性。
与原始的snowflake算法不同在于,uid-generator支持自定义时间戳、工作机器ID和 序列号 等各部分的位数,而且uid-generator中采用用户自定义workId的生成策略。
代码示例:

import com.baidu.fsg.uid.UidGenerator;  
import com.baidu.fsg.uid.impl.CachedUidGenerator;  public class UidGeneratorDemo {  public static void main(String[] args) {  // 创建一个UidGenerator实例  UidGenerator uidGenerator = new CachedUidGenerator();  // 初始化,这里只是一个简单的示例,实际使用时你可能需要根据你的业务场景进行更复杂的配置  // 例如,设置workerId、epoch等  // 注意:在多实例部署时,每个实例的workerId必须唯一  long workerId = 1L; // 示例ID,实际使用时需要保证每个实例的唯一性  long datacenterId = 1L; // 数据中心ID,示例  uidGenerator.init(workerId, datacenterId, null);  // 生成一个UID  long uid = uidGenerator.getUID();  System.out.println("Generated UID: " + uid);  }  
}

7. 基于Zookeeper的顺序节点

利用Zookeeper的顺序节点特性来生成全局唯一ID。

优点:
● 利用Zookeeper的集群特性保证高可用。
● ID全局唯一。
缺点:
● 需要依赖Zookeeper集群。
● 可能会受到Zookeeper性能的限制。
● 并发竞争较大不适合用Zookeeper

8. 数据库集群模式

单库的数据库自增ID会存在单点问题,所以可以用数据库集群模式,去解决这个问题。数据库集群模式:通过多个数据库实例设置不同的起始值和步长来生成全局唯一的ID。

数据库集群模式优点:
● 可以有效生成集群中的唯一ID。解决了单点的问题。
● 降低ID生成数据库操作的负载。
数据库集群模式缺点:
● 需要独立部署多个数据库实例,成本高。
● 后期不方便扩展

9. 美团(Leaf)

Leaf是美团点评开源的分布式ID生成系统,包含基于数据库和基于Zookeeper的两种实现方式。

以基于数据库的自增ID生成策略为例(数据库表结构):
CREATE TABLE leaf_alloc (
biz_tag VARCHAR(128) NOT NULL COMMENT ‘业务key’,
max_id BIGINT(20) NOT NULL COMMENT ‘当前已分配的最大id’,
step INT(11) NOT NULL COMMENT ‘每次id的增长步长’,
PRIMARY KEY (biz_tag)
) ENGINE=INNODB DEFAULT CHARSET=utf8mb4;
Java 实现:

import java.sql.*;  public class LeafIdGenerator {  private static final String JDBC_URL = "jdbc:mysql://localhost:3306/your_database?useSSL=false&serverTimezone=UTC";  private static final String USERNAME = "your_username";  private static final String PASSWORD = "your_password";  private static final String UPDATE_SQL = "UPDATE leaf_alloc SET max_id = max_id + ? WHERE biz_tag = ?";  private static final String SELECT_SQL = "SELECT max_id FROM leaf_alloc WHERE biz_tag = ? FOR UPDATE";  public synchronized long getId(String bizTag) throws SQLException {  Connection conn = null;  PreparedStatement updateStmt = null;  PreparedStatement selectStmt = null;  ResultSet rs = null;  try {  conn = DriverManager.getConnection(JDBC_URL, USERNAME, PASSWORD);  selectStmt = conn.prepareStatement(SELECT_SQL);  selectStmt.setString(1, bizTag);  rs = selectStmt.executeQuery();  if (rs.next()) {  long maxId = rs.getLong("max_id");  int step = 1000; // 假设步长为1000,你可以从数据库中读取这个值  // 假设这里只是简单演示,不检查是否超过max_id + step是否溢出  updateStmt = conn.prepareStatement(UPDATE_SQL);  updateStmt.setInt(1, step);  updateStmt.setString(2, bizTag);  updateStmt.executeUpdate();  // 返回ID区间中的一个ID,这里简单返回maxId(实际应用中可能需要更复杂的策略)  return maxId;  } else {  // 如果没有找到对应的bizTag,则需要初始化  // ... 初始化代码省略 ...  throw new RuntimeException("BizTag not found: " + bizTag);  }  } finally {  // 关闭资源,省略了异常处理  if (rs != null) rs.close();  if (selectStmt != null) selectStmt.close();  if (updateStmt != null) updateStmt.close();  if (conn != null) conn.close();  }  }  public static void main(String[] args) {  LeafIdGenerator generator = new LeafIdGenerator();  try {  long id = generator.getId("test-biz-tag");  System.out.println("Generated ID: " + id);  } catch (SQLException e) {  e.printStackTrace();  }  }  
}

优点:
● 结合了数据库和Zookeeper的优点,提供了高可用和高性能的ID生成服务。缺点:
● 就是时钟回拨问题、复杂性高。

分布式 ID 生成系统 Leaf
Leaf生成分布式ID的原理
Leaf(叶子)是美团点评开源的一个分布式 ID 生成系统,它的原理基于 Snowflake 算法。下面是 Leaf 生成分布式 ID 的基本原理:
Snowflake 算法:
Leaf 使用了 Snowflake 算法作为 ID 生成的基础。Snowflake 算法是一种利用时间、机器ID和序列号生成唯一ID的算法。
Snowflake 算法的结构一般包括一个 64 位的整数,其中高位是时间戳,中间部分是机器ID,最后一部分是序列号。
组成部分:
时间戳:用来记录生成 ID 的时间,通常精确到毫秒级别。
机器ID:标识不同的机器,确保分布式环境下的唯一性。
序列号:在同一毫秒内,通过序列号确保生成的 ID 不重复。
Leaf 的实现:
Leaf 会分配一个全局唯一的机器ID,通常由配置文件指定。
每次生成 ID 时,Leaf 会获取当前时间戳,与上一次生成 ID 的时间戳进行比较,如果相同则递增序列号;如果不同则重置序列号为 0。
最后将时间戳、机器ID和序列号合并生成一个唯一的分布式 ID。
优点:
Leaf 生成的 ID 具有趋势递增、唯一性和高性能的特点。
通过机器ID的划分,可以支持多台机器生成唯一的ID,适用于分布式系统中的 ID 生成需求。

总的来说,Leaf 基于 Snowflake 算法实现了一个高效、高性能的分布式 ID 生成系统,通过合理地利用时间戳、机器ID和序列号,确保生成的 ID 在分布式环境下唯一且趋势递增。这样的设计方案可以满足大多数分布式系统对于唯一 ID 的需求。

10. 滴滴(Tinyid)

Tinyid是滴滴开源的轻量级分布式ID生成系统,它是基于号段模式原理实现的与Leaf如出一辙,每个服务获取一个号段(1000,2000]、(2000,3000]、(3000,4000]

以下是一个简化的Tinyid,服务端的伪代码:

// 假设我们有一个ID生成器,这里用AtomicLong模拟  
import java.util.concurrent.atomic.AtomicLong;  public class TinyidService {  private AtomicLong idGenerator = new AtomicLong(0);  // 模拟的ID生成方法  public synchronized long generateId() {  return idGenerator.incrementAndGet();  }  // 这里应该是RESTful API的实现,但为简化起见,我们省略了HTTP部分  // 客户端应该通过HTTP请求调用此方法  public long getIdOverHttp() {  return generateId();  }  
}

客户端(Java示例)

import okhttp3.*;  public class TinyidClient {  private static final String TINYID_SERVICE_URL = "http://localhost:8080/tinyid/generate";  public static void main(String[] args) {  OkHttpClient client = new OkHttpClient();  Request request = new Request.Builder()  .url(TINYID_SERVICE_URL)  .build();  client.newCall(request).enqueue(new Callback() {  @Override  public void onFailure(Call call, IOException e) {  e.printStackTrace();  }  @Override  public void onResponse(Call call, Response response) throws IOException {  if (!response.isSuccessful()) {  throw new IOException("Unexpected code " + response);  } else {  // 假设服务端返回的是纯文本格式的ID  String responseBody = response.body().string();  long id = Long.parseLong(responseBody);  System.out.println("Generated ID: " + id);  }  }  });  }  
}

● 优缺点:简单、轻量级,但性能可能不如其他方案。

http://www.xdnf.cn/news/650251.html

相关文章:

  • 解决虚拟机挂起后,docker容器无法访问的问题
  • Qt6无法识别OpenCV(Windows端开发)
  • 【RabbitMQ】基于Spring Boot + RabbitMQ 完成应用通信
  • 七、【前端路由篇】掌控全局:Vue Router 实现页面导航、动态路由与权限控制
  • 2025/5/26 学习日记 基本/扩展正则表达式 linux三剑客之grep
  • [ARM][架构] 02.AArch32 程序状态
  • 3DVR拍摄指南:从理论到实践
  • [特殊字符] next-intl 服务端 i18n getTranslations 教程
  • 三分钟了解 MCP 概念(Model Context Protocol,模型上下文协议)
  • CLAM完整流程。patches-feature-split-train-eval
  • 5.26 面经整理 360共有云 golang
  • Java大师成长计划之第31天:Docker与Java应用容器化
  • 基于matlab版本的三维直流电法反演算法
  • 论文阅读: 2023 NeurIPS Jailbroken: How does llm safety training fail?
  • 支持selenium的chrome driver更新到136.0.7103.113
  • C++寻位映射的究极密码:哈希扩展
  • ubuntu 22.04 配置静态IP、网关、DNS
  • 鸿蒙OSUniApp 实现的日期选择器与时间选择器组件#三方框架 #Uniapp
  • 对数的运算困惑
  • 鸿蒙OSUniApp 开发带有通知提示的功能组件#三方框架 #Uniapp
  • Linux《基础IO》
  • 深入Java TCP流套接字编程:高效服务器构建与高并发实战优化指南​
  • Kafka自定义分区策略实战避坑指南
  • 论文阅读笔记:YOLO-World: Real-Time Open-Vocabulary Object Detection
  • nginx安全防护与https部署实战
  • 简述各类机器学习问题
  • 机器学习k近邻,高斯朴素贝叶斯分类器
  • html使用JS实现账号密码登录的简单案例
  • uboot常用命令之eMMC/SD卡命令
  • rpm安装jenkins-2.452