分布式ID

文章目录

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

  1. 雪花算法(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);  
                }  
            }  
        });  
    }  
}

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

相关推荐
退役小学生呀8 小时前
十九、云原生分布式存储 CubeFS
分布式·docker·云原生·容器·kubernetes·k8s
smileNicky9 小时前
Kafka 为什么具有高吞吐量的特性?
分布式·kafka
小白不想白a15 小时前
【Hadoop】HDFS 分布式存储系统
hadoop·分布式·hdfs
随心............16 小时前
Spark面试题
大数据·分布式·spark
Hello.Reader18 小时前
用一根“数据中枢神经”串起业务从事件流到 Apache Kafka
分布式·kafka·apache
找不到、了1 天前
常用的分布式ID设计方案
java·分布式
AKAMAI2 天前
在分布式计算区域中通过VPC搭建私有网络
人工智能·分布式·云计算
面带微笑向前走2 天前
分布式集群压测+grafana+influxdb+Prometheus详细步骤
分布式·grafana·prometheus
何中应2 天前
分布式事务的两种解决方案
java·分布式·后端
诸葛务农2 天前
人形机器人——电子皮肤技术路线:光学式电子皮肤及MIT基于光导纤维的分布式触觉传感电子皮肤
分布式·机器人·wpf