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日

相关文章

  • IDEA 如何导入别人的javaweb项目进行部署

    下面是在 IDEA 中导入别人的 JavaWeb 项目并进行部署的详细攻略: 步骤1:下载并安装 IDEA 如果您还没有安装 IDEA,可以到 IntelliJ IDEA 官网下载对应版本并安装。安装过程中请按照提示一步一步操作即可。 步骤2:下载并解压缩 JavaWeb 项目 假设您已经获得了别人的 JavaWeb 项目源代码,接下来需要将其解压缩到本地。…

    Java 2023年6月2日
    00
  • 什么是线程安全?

    以下是关于线程安全的完整使用攻略: 什么是线程安全? 线程安全是指在多线程环境下,对共享资源的访问不会出现数据不一致或者数据污染的问题。在多线程环境下,如果多个线程同时访问同一个共享资源,那么就有可能出现数据不一致的问题,这就是线程安全。 为了保证线程安全,需要采取一些措施,比如使用同步机制、使用线程安全的结构等。 1. 同步机制 同步机制是指在多线程环境下…

    Java 2023年5月12日
    00
  • Java读写Windows共享文件夹的方法实例

    我来为你讲解一下“Java读写Windows共享文件夹的方法实例”的完整攻略。 1. 导入jar包 在项目中导入jt400.jar包。该jar包中包含了Java对Windows共享文件夹的操作类。 <dependency> <groupId>com.ibm.as400</groupId> <artifactId&gt…

    Java 2023年5月19日
    00
  • Java多线程下的其他组件之CyclicBarrier、Callable、Future和FutureTask详解

    Java多线程下的其他组件之CyclicBarrier CyclicBarrier概述 CyclicBarrier是Java中一个同步工具类,用于在多线程中等待所有线程到达某个同步点,然后再一起执行后续操作,这个同步点就是所谓的屏障(barrier),它可重用,即当到达屏障的线程数量达到指定值时,所有线程都可以通过屏障,继续执行下一个操作。 CyclicBa…

    Java 2023年5月18日
    00
  • 解读Tomcat启动、重启、暂停操作(window)

    我来为您详细讲解“解读Tomcat启动、重启、暂停操作(window)”的完整攻略。 1. Tomcat启动操作 1.1. 检查JDK环境变量 首先要检查JDK 的环境变量设置是否正确。具体来说,需要检查以下环境变量: JAVA_HOME:JDK的安装目录路径。 CLASSPATH:Java运行时使用的类搜索路径。 PATH:系统的环境变量,需要将%JAVA…

    Java 2023年5月19日
    00
  • Java对MySQL数据库进行连接、查询和修改操作方法

    关于“Java对MySQL数据库进行连接、查询和修改操作方法”的完整攻略,我们可以以下列步骤进行: 1. 下载MySQL的JDBC驱动器 Java需要使用MySQL连接器(JDBC驱动器)才能连接MySQL服务器。你可以从MySQL官网上找到驱动器并下载。 下载的链接是:https://dev.mysql.com/get/Downloads/Connecto…

    Java 2023年5月20日
    00
  • struts2+spring+hibernate分页代码[比较多]第1/7页

    下面我来为你详细讲解“struts2+spring+hibernate分页代码[比较多]第1/7页”的完整攻略。 概述 该攻略主要涉及到使用struts2、spring、hibernate等框架进行分页的操作。在该攻略中,我们将使用分页插件完成分页操作,具体实现过程如下。 步骤 引入分页插件 我们可以通过Maven引入pagehelper插件,具体配置如下:…

    Java 2023年5月20日
    00
  • php 什么是PEAR?

    PHP 什么是PEAR? PEAR(PHP Extension and Application Repository)是 PHP 的扩展与应用程序仓库,是一个官方的、由 PHP 社区运行的开源项目,旨在为 PHP 开发人员提供高质量的可重用代码和可重用组件。PEAR 从软件设计的角度出发,提倡“以面向对象方式设计,尽可能复用已有的代码片段” 的编码风格,简化…

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