分布式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);  
                }  
            }  
        });  
    }  
}

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

相关推荐
今天又在摸鱼4 小时前
rabbitmq的高级特性
分布式·rabbitmq
LDM>W<8 小时前
黑马点评-分布式锁Lua脚本
java·分布式·lua
CET中电技术10 小时前
分布式光伏接入引起农村电压越限,如何处理?
分布式·光伏
鲸屿19511 小时前
kafka之操作示例
分布式·kafka
山猪打不过家猪13 小时前
(五)MMA(OpenTelemetry/Rabbit MQ/)
分布式·缓存
小小工匠13 小时前
分布式缓存:ZSET → MGET 跨槽(cross‐slot)/ 并发 GET解决思路
分布式·缓存·zset跨槽·mget跨槽·并发get
zhangxzq13 小时前
华为高斯数据库(GaussDB)深度解析:国产分布式数据库的旗舰之作
数据库·分布式·gaussdb
时序数据说13 小时前
时序数据库IoTDB启动方式及集群迁移指南
大数据·数据库·分布式·开源·php·时序数据库·iotdb
莱茵不哈哈14 小时前
etcd:高可用,分布式的key-value存储系统
数据库·分布式·go·etcd·kv
IT成长日记16 小时前
【Doris入门】Doris初识:分布式分析型数据库的核心价值与架构解析
数据库·分布式·架构·doris