java基于数据库实现全局唯一ID的示例

以下是“java基于数据库实现全局唯一ID的示例”的完整攻略及两条示例:

一、前置条件

在进行本教程之前,请确保以下条件已经满足:

  • 你已熟悉Java编程语言,并且能够独立编写Java代码;
  • 你已经安装了MySQL数据库,并掌握了基本操作;
  • 你已经安装了Java开发环境和相关依赖库。

二、方案选择

目前常见的实现全局唯一ID的方案有雪花算法、UUID等。本教程将以雪花算法为例,介绍基于数据库实现全局唯一ID的方法。

三、实现过程

1.创建数据库

首先,需要在MySQL数据库中创建一个表,用于存储唯一ID的信息。

CREATE TABLE `t_unique_id`
(
  `id` BIGINT(20) UNSIGNED NOT NULL COMMENT 'ID' PRIMARY KEY AUTO_INCREMENT,
  `gmt_create` DATETIME NOT NULL COMMENT '创建时间',
  `data_center_id` INT(11) UNSIGNED NOT NULL COMMENT '数据中心ID',
  `machine_id` INT(11) UNSIGNED NOT NULL COMMENT '机器ID',
  `sequence` INT(11) UNSIGNED NOT NULL COMMENT '序列号',
  UNIQUE KEY `uk_data_center_id_machine_id_sequence` (`data_center_id`, `machine_id`, `sequence`)
) ENGINE=INNODB DEFAULT CHARSET=utf8mb4 COMMENT='唯一ID生成记录表';

说明:

  • id:主键,唯一标识符。
  • gmt_create:记录创建时间。
  • data_center_id:数据中心ID,用于标识数据中心(最多支持32个数据中心,每个数据中心最多支持1024台机器)。
  • machine_id:机器ID,用于标识某一台机器。
  • sequence:序列号,用于标识某一毫秒内生成的ID序列。

其中,data_center_idmachine_idsequence三列加上唯一索引,用于保证同一时刻生成的ID不重复。

2.编写Java代码

import java.sql.*;
import java.util.concurrent.atomic.AtomicLong;

public class UniqueIdGenerator {

    // 数据中心ID
    private final long dataCenterId;
    // 机器ID
    private final long machineId;
    // 序列号
    private final AtomicLong sequence = new AtomicLong(0L);

    // 构造函数
    public UniqueIdGenerator(long dataCenterId, long machineId) {
        this.dataCenterId = dataCenterId;
        this.machineId = machineId;
    }

    // 获取唯一ID
    public long nextId() throws SQLException {
        while (true) {
            try (Connection connection = getConnection()) {
                // 获取当前时间戳
                long timestamp = System.currentTimeMillis();
                // 从数据库中查询记录
                PreparedStatement statement = connection.prepareStatement("SELECT sequence FROM t_unique_id WHERE data_center_id = ? AND machine_id = ?");
                statement.setLong(1, dataCenterId);
                statement.setLong(2, machineId);
                ResultSet resultSet = statement.executeQuery();
                if (resultSet.next()) {
                    // 如果数据库中已经存在记录,则更新记录的序列号
                    long sequence = resultSet.getLong(1);
                    if (isValid(timestamp, sequence)) {
                        update(sequence + 1);
                        return ((timestamp - 1400000000000L) << 22) | (dataCenterId << 17) | (machineId << 12) | (sequence + 1);
                    }
                } else {
                    // 如果数据库中不存在记录,则插入一条新的记录,并返回唯一ID
                    boolean inserted = insert();
                    if (inserted) {
                        return ((timestamp - 1400000000000L) << 22) | (dataCenterId << 17) | (machineId << 12) | 1;
                    }
                }
            }

            // 等待下一毫秒
            try {
                Thread.sleep(1L);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    // 更新记录的序列号
    private void update(long sequence) throws SQLException {
        try (Connection connection = getConnection()) {
            PreparedStatement statement = connection.prepareStatement("UPDATE t_unique_id SET sequence = ? WHERE data_center_id = ? AND machine_id = ?");
            statement.setLong(1, sequence);
            statement.setLong(2, dataCenterId);
            statement.setLong(3, machineId);
            statement.executeUpdate();
        }
    }

    // 插入新的记录
    private boolean insert() throws SQLException {
        try (Connection connection = getConnection()) {
            PreparedStatement statement = connection.prepareStatement("INSERT INTO t_unique_id (gmt_create, data_center_id, machine_id, sequence) VALUES (?, ?, ?, ?)");
            statement.setTimestamp(1, new Timestamp(System.currentTimeMillis()));
            statement.setLong(2, dataCenterId);
            statement.setLong(3, machineId);
            statement.setLong(4, 1L);
            return statement.executeUpdate() > 0;
        }
    }

    // 获取数据库连接
    private Connection getConnection() throws SQLException {
        String url = "jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf8mb4&serverTimezone=GMT%2B8";
        String username = "root";
        String password = "password";
        return DriverManager.getConnection(url, username, password);
    }

    // 判断是否有效
    private boolean isValid(long timestamp, long sequence) {
        long currentTimestamp = System.currentTimeMillis();
        return timestamp - currentTimestamp <= 1L && sequence <= 1023L;
    }

}

说明:

  • dataCenterIdmachindIdsequence分别表示数据中心ID、机器ID和序列号,其中sequence使用AtomicLong实现线程安全递增。
  • nextId方法用于获取一个唯一ID,采用了数据库交互方式实现。首先从数据库中查询是否有记录,如果有则更新记录的序列号;如果没有则插入一条新的记录,并返回唯一ID。注意:需要在一个毫秒内保证生成的唯一ID是不重复的。
  • update方法用于更新记录的序列号。
  • insert方法用于插入新的记录。
  • getConnection方法用于获取数据库连接。
  • isValid方法用于判断记录是否有效,保证同一毫秒内生成的ID不重复。

3.测试

以下是两条测试代码:

示例 1:单线程测试

public class UniqueIdGeneratorTest {

    @Test
    public void test() throws SQLException {
        UniqueIdGenerator generator = new UniqueIdGenerator(1L, 1L);
        long id = generator.nextId();
        System.out.println(id);
        Assert.assertNotEquals(0L, id);
    }

}

说明:

  • 构造函数中的参数1L1L分别表示数据中心ID和机器ID。
  • 测试代码只是简单调用,并输出ID。

示例 2:多线程测试

public class UniqueIdGeneratorTest {

    @Test
    public void test() throws SQLException, InterruptedException, ExecutionException {
        int threadCount = 100;
        ExecutorService executorService = Executors.newFixedThreadPool(threadCount);
        List<Future<Long>> futures = new ArrayList<>();
        for (int i = 0; i < threadCount; i++) {
            Future<Long> future = executorService.submit(() -> {
                UniqueIdGenerator generator = new UniqueIdGenerator(1L, 1L);
                return generator.nextId();
            });
            futures.add(future);
        }
        Set<Long> ids = new HashSet<>();
        for (Future<Long> future : futures) {
            long id = future.get();
            System.out.println(id);
            Assert.assertNotEquals(0L, id);
            Assert.assertFalse(ids.contains(id));
            ids.add(id);
        }
    }

}

说明:

  • threadCount表示测试的线程数。
  • 使用线程池同时生成多个唯一ID,并将生成的ID保存到HashSet中,用于判断是否出现重复ID。

四、总结

通过本教程,我们实现了基于数据库的全局唯一ID,具有以下特点:

  • 算法简单,实现成本低;
  • 毫秒内保证ID的唯一性,效率高;
  • 支持多个数据中心和多台机器,灵活性强。

该方案可以广泛应用于分布式系统中,例如订单号、流水号等业务场景。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:java基于数据库实现全局唯一ID的示例 - Python技术站

(0)
上一篇 2023年5月20日
下一篇 2023年5月20日

相关文章

  • Springboot使用Security实现OAuth2授权验证完整过程

    下面我为大家详细讲解Spring boot使用Security实现OAuth2授权验证的完整流程。 1. OAuth2介绍 OAuth2是一种常用的授权框架,可以使得第三方应用程序获得用户的授权才能访问用户的资源。OAuth2的主要授权方式有4种: 1.1 授权码模式(Authorization Code) 授权码模式是OAuth2中最常用的一种模式。其要求…

    Java 2023年5月20日
    00
  • JavaBeans程序开发

    JavaBeans程序开发攻略 什么是JavaBeans JavaBeans是Java语言编写的可重用组件,用于构建应用程序。JavaBeans遵循特定的设计模式和规范,通常包含一个无参构造器和一些getter和setter方法,使它们易于使用和维护。 开发JavaBeans的步骤 定义JavaBean的类和属性 为属性添加对应的getter和setter方…

    Java 2023年5月23日
    00
  • Log4j2 重大漏洞编译好的log4j-2.15.0.jar包下载(替换过程)

    下面我将给出完整的攻略,以便您正确地处理Log4j2重大漏洞。 1. 检测漏洞 首先,您需要检测您的应用程序是否存在Log4j2漏洞。您可以使用以下命令进行检测: java -jar log4j-cve-2021-44228-scanner.jar <your_application_jar_file(s)> 如果命令返回以下信息,则说明您的应用…

    Java 2023年6月2日
    00
  • java string的一些细节剖析

    Java String的一些细节剖析 基本概念 Java中的字符串是由多个字符组成的,可以通过String类进行实现。Java字符串有一些独特的性质,值得我们深入研究。 创建字符串 Java中创建字符串的常用方式有: String str1 = "Hello"; String str2 = new String("World&q…

    Java 2023年6月1日
    00
  • 详细聊一聊java中封装的那点事

    接下来我将为大家讲解“详细聊一聊 Java 中封装的那点事”的攻略。 什么是封装? 封装是面向对象编程中的三大特性之一,它是指隐藏对象的属性和实现细节,仅对外部暴露一些必要的接口来与外部交互,这样可以更好地保护对象的数据,避免不必要的访问和修改。 在 Java 中,通常使用访问修饰符来实现封装,包括:public(公有的)、private(私有的)和 pro…

    Java 2023年5月26日
    00
  • 5个JAVA入门必看的经典实例

    下面我将详细讲解“5个JAVA入门必看的经典实例”的完整攻略。 1. 介绍 作为一名Java入门者,学好基础知识是必不可少的。在学习Java的过程中,掌握经典实例是非常重要的,可以帮助我们深入理解Java的基本语法和编程思想。本文总结了5个Java入门必看的经典实例,帮助初学者掌握Java编程的基本技能。 2. 经典实例1:计算圆的面积 第一个经典实例是计算…

    Java 2023年5月19日
    00
  • JAVA+Hibernate 无限级分类

    我可以为你详细讲解“JAVA+Hibernate 无限级分类”的完整攻略。这个攻略的目的是帮助Java开发者使用Hibernate实现无限级分类(即树形结构),以便更高效地组织和管理数据。 什么是无限级分类? 无限级分类又称为多级分类或树形结构分类,是指将一个分类体系无限地层层递进,其中每一项都可以作为父级和子级同时存在。常见的例子包括商品分类、地理位置管理…

    Java 2023年5月19日
    00
  • mybatis输入映射和输出映射实例详解

    MyBatis输入映射和输出映射实例详解 MyBatis是一款基于Java的持久层框架,可以通过Mapper XML文件定义SQL语句及其输入输出参数。在Mapper XML文件中,输入映射用于将Java对象转换为SQL语句中的参数,输出映射用于将查询结果转换为Java对象。接下来我们将介绍MyBatis输入映射和输出映射的详细步骤。 输入映射 输入映射用于…

    Java 2023年5月26日
    00
合作推广
合作推广
分享本页
返回顶部