生成唯一的ID

在Java开发中,我们经常会遇到需要生成唯一的ID的情况,比如数据库中的主键、分布式系统中的事务ID等。本文将介绍几种生成唯一ID的常用方法,并提供相应的代码示例。

UUID

UUID(Universally Unique Identifier)是一个标准的32位长度的字符串,它可以用来生成唯一的ID。Java中可以使用java.util.UUID类来生成UUID。

import java.util.UUID;

public class UUIDExample {
    public static void main(String[] args) {
        UUID uuid = UUID.randomUUID();
        System.out.println(uuid.toString());
    }
}

上述代码会输出一个类似于550e8400-e29b-41d4-a716-446655440000的字符串,这就是一个UUID。UUID的生成是基于当前时间、计算机的硬件地址和其他唯一的标识符来实现的,基本上可以保证生成的ID是唯一的。但是UUID的长度较长,不适合作为数据库的主键。

自增ID

自增ID是指一个递增的整数,可以通过数据库的自增主键来实现。在Java中,可以使用数据库的自增主键来生成唯一的ID。

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.Statement;

public class AutoIncrementIDExample {
    public static void main(String[] args) {
        try {
            Connection conn = DriverManager.getConnection("jdbc:mysql://localhost/test", "username", "password");
            Statement stmt = conn.createStatement();
            String sql = "INSERT INTO users (name) VALUES ('John')";
            stmt.executeUpdate(sql, Statement.RETURN_GENERATED_KEYS);
            ResultSet rs = stmt.getGeneratedKeys();
            if (rs.next()) {
                int id = rs.getInt(1);
                System.out.println(id);
            }
            rs.close();
            stmt.close();
            conn.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

上述代码通过JDBC连接到数据库,并执行一条插入语句,获取自增主键的值作为唯一ID。这种方法在分布式系统中不适用,因为每个节点都需要连接到数据库来生成ID,可能会引起性能问题。

雪花算法

雪花算法(Snowflake)是Twitter开源的一种生成唯一ID的算法,它可以在分布式系统中生成有序的、唯一的ID。雪花算法的ID是一个64位的整数,由以下几部分组成:

  • 时间戳(41位):精确到毫秒级别的时间戳,可以使用当前时间减去一个起始时间戳来得到相对时间,可以使用System.currentTimeMillis()来获取当前时间戳。
  • 机器ID(10位):机器的唯一标识符,可以通过将机器的IP地址转换为一个数字来得到。
  • 序列号(12位):同一毫秒内生成的ID的序列号,从0开始递增,如果同一毫秒生成的ID数量超过4096,可以等待下一毫秒再继续生成。

下面是一个使用雪花算法生成唯一ID的示例代码:

public class SnowflakeIDExample {
    private static final long START_TIMESTAMP = 1635148800000L;
    private static final long WORKER_ID_BITS = 10L;
    private static final long MAX_WORKER_ID = ~(-1L << WORKER_ID_BITS);
    private static final long SEQUENCE_BITS = 12L;
    private static final long WORKER_ID_SHIFT = SEQUENCE_BITS;
    private static final long TIMESTAMP_SHIFT = SEQUENCE_BITS + WORKER_ID_BITS;
    private static final long SEQUENCE_MASK = ~(-1L << SEQUENCE_BITS);
    
    private long workerId;
    private long sequence = 0L;
    private long lastTimestamp = -1L;
    
    public SnowflakeIDExample(long workerId) {
        if (workerId > MAX_WORKER_ID || workerId < 0) {
            throw new IllegalArgumentException(String.format("Worker ID can't be greater than %d or less than 0", MAX_WORKER_ID));
        }
        this.workerId = workerId;
    }
    
    public synchronized long nextId() {
        long timestamp = System.currentTimeMillis();
        if (timestamp < lastTimestamp) {
            throw new RuntimeException("Clock moved backwards. Refusing to generate ID");
        }
        
        if (timestamp == lastTimestamp) {
            sequence = (sequence + 1) & SEQUENCE_MASK;
            if (sequence ==