Java大厂面试题之10种分布式ID的生成方案

news/2025/1/11 12:58:13/

一、前言

日常工作中,我们开发的系统、或者中间件,都是分布式部署的。比如你的订单数据库,做了分库分表,这时候,你需要一个唯一的ID来标记一条数据。这时候,就需要分布式ID。分布式ID是在分布式系统下使用的ID,用于在多个节点中生成全局唯一的标识符。

二、UUID

UUID是通用唯一识别码(Universally Unique Identifier)的缩写。它通过一定的算法机器生成,包括网卡MAC地址、时间戳、名字空间(Namespace)、随机或伪随机数、时序等元素。

代码示例:

 UUID randomUUID = UUID.randomUUID();System.out.println(randomUUID);//输出示例:a4332986-c548-4a39-9202-c82e2ea3b455

1、UUID的优点: 

  • 本地生成ID,不需要进行远程调用,时延低,性能高。

  • 全球唯一,数据迁移容易。

2、UUID的缺点:

  • UUID过长,生成的是字符串,存储空间高。

  • 不是有序的,无法保证趋势递增。

  • 可读性差,不体现业务信息。

三、 数据库自增ID

利用数据库的自增策略,如MySQL的auto_increment,来实现分布式ID。

1、数据库自增ID的优点: 

  • 主键自动增长,不用手工设值。

  • 数字型,占用空间小、检索有利。

  • 绝对有序。

2、数据库自增ID的缺点:

  • 并发性能不高,受限于数据库性能。

  • 不太适合重构的系统,因为涉及旧数据迁移容易ID冲突。

  • 暴露商业信息,例如可以推断出来订单量。

  • 存在单点问题

四、数据库集群模式

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

1、数据库集群模式优点: 

  • 可以有效生成集群中的唯一ID。解决了单点的问题。

  • 降低ID生成数据库操作的负载。

2、数据库集群模式缺点:

  • 需要独立部署多个数据库实例,成本高。

  • 后期不方便扩展

五、Redis实现的分布式ID

我们可以利用,Redis的自增命令incr生成全局唯一ID。具体实现方式是:在Redis中维护一个自增的计数器,每次生成ID时,从Redis中获取计数器的值,然后将其加一并更新回Redis。

这种方式实现的分布式ID,是唯一的,并且是有序的,我们可以拼接时间、机器标识、业务类型等,让这个ID更有特殊意思。 

Redis实现的分布式ID优点:

  • 高性能、可扩展性强

  • 唯一有序的

Redis实现的分布式ID缺点:

  • 需要依赖Redis集群,否则存在单点问题。

  • 可能存在ID冲突的风险(如果Redis集群设计不当)。

六、 Snowflake雪花算法

雪花算法是一种生成分布式全局唯一ID的算法,生成的ID称为Snowflake IDs。这种算法由Twitter创建,并用于推文的ID。

一个Snowflake ID有64位。

  • 第1位:Java中long的最高位是符号位代表正负,正数是0,负数是1,一般生成ID都为正数,所以默认为0。
  • 接下来前41位是时间戳,表示了自选定的时期以来的毫秒数。
  • 接下来的10位代表计算机ID,防止冲突。
  • 其余12位代表每台机器上生成ID的序列号,这允许在同一毫秒内创建多个Snowflake ID。

Snowflake雪花算法的优点:

  • 生成的ID全局唯一、趋势递增。

  • 性能高,可扩展性强。

Snowflake雪花算法的缺点:

  • 需要时钟回拨处理机制。

  • 依赖机器ID和数据中心ID的分配。

七、基于Zookeeper的顺序节点

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

优点:

  • 利用Zookeeper的集群特性保证高可用。

  • ID全局唯一。

缺点:

  • 需要依赖Zookeeper集群。

  • 可能会受到Zookeeper性能的限制。

  • 并发竞争较大不适合用Zookeeper

八、百度的uid-generator

基于Twitter的Snowflake算法进行改进,增加了更多的配置和灵活性。

与原始的snowflake算法不同在于,uid-generator支持自定义时间戳、工作机器ID和 序列号 等各部分的位数,而且uid-generator中采用用户自定义workId的生成策略。

代码demo:

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);  }  
}
  • 优缺点:类似Snowflake算法,但配置更灵活。

 九、美团(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生成服务。

缺点:

  • 就是时钟回拨问题、复杂性高。

十、滴滴(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.ppmy.cn/news/1561928.html

相关文章

从 2D 图像中学习 3D 人机交互关系

一、论文背景与动机 理解3D人类与物体的交互关系(3D Human-Object Interaction Relation, 3D HOI)对于具身智能和交互建模至关重要。然而,现有的大多数方法通常通过单独预测某些交互元素(如人类接触点、物体可供性、人-物空间关系…

【TI毫米波雷达】DCA1000不使用mmWave Studio的数据采集方法,以及自动化实时数据采集

【TI毫米波雷达】DCA1000不使用mmWave Studio的数据采集方法,以及自动化实时数据采集 mmWave Studio提供的功能完全够用了 不用去纠结用DCA1000低延迟、无GUI传数据 速度最快又保证算力无非就是就是Linux板自己写驱动做串口和UDP 做雷达产品应用也不会采用DCA1000的…

《分布式光纤测温:解锁楼宇安全的 “高精度密码”》

在楼宇建筑中,因其内部空间庞大,各类电器设施众多,如何以一种既高效又稳定,兼具低成本与高覆盖特性的方式,为那些关键线路节点开展温度监测,是目前在安全监测领域一项重点研究项目,而无锡布里渊…

4 驱动开发

环境配置 开发环境 在开发驱动程序之前,我们需要配置好开发环境, 首先安装好VS IDE(这里自己选择版本),其次因为我们需要开发驱动程序所以需要安装WDK(WDK下载地址:以前的 WDK 版本和其他下载 - Windows drivers | Microsoft Learn),在我们安装WDK时候需要注意其版本…

2025届视觉算法开发工程师面试问题汇总

2025届视觉算法开发工程师面试问题汇总 1. 数据结构1.1 时间复杂度 2. 算法问题2.1 两数之和2.2 递归求二叉树的深度2.3 一个由0和1组成数组中,计算出这里面连续1的最大数 3. C、Python、Cuda问题3.1 C3.1.1 智能指针3.1.1.1 std::unique_ptr独占指针3.1.1.2 std::s…

内蒙古水系详细很全shp格式arcgis软件无偏移坐标下载后内容测评

标题中的“内蒙古水系详细很全shp格式arcgis软件无偏移坐标”指的是一个地理信息系统(GIS)数据集,该数据集详细记录了内蒙古地区的水系信息,并以ESRI公司的标准矢量数据格式——Shapefile(.shp)进行存储。S…

C++虚函数(八股总结)

什么是虚函数 虚函数是在父类中定义的一种特殊类型的函数,允许子类重写该函数以适应其自身需求。虚函数的调用取决于对象的实际类型,而不是指针或引用类型。通过将函数声明为虚函数,可以使继承层次结构中的每个子类都能够使用其自己的实现&a…

第14章 MySQL事务日志

第14章 MySQL事务日志 事务有4种特性:原子性、一致性、隔离性和持久性。那么事务的四种特性到底是基于什么机制实现呢? 事务的隔离性由锁机制实现。而事务的原子性、一致性和持久性由事务的 redo 日志和undo 日志来保证。 REDO LOG 称为重做日志&#…