示例代码:
import java.util.UUID;public class UUIDDemo {public static void main(String[] args) {// 生成一个随机UUIDUUID uuid = UUID.randomUUID();System.out.println("随机生成的UUID为:" + uuid.toString());}
}
运行结果:
随机生成的UUID为:f43e4fc8-89fc-491e-bd44-c7c6a5dc09ca
以上代码中,导入了Java的java.util.UUID
库,利用UUID.randomUUID()
方法生成一个随机UUID并输出。
基于时间戳的全局唯一ID(Snowflake算法):通过组合机器ID、时间戳和序列号等信息生成唯一ID。
public class SnowflakeDemo {// 机器ID(可以使用配置文件读取)private static final long WORKER_ID = 1L;// 数据中心ID(可以使用配置文件读取)private static final long DATA_CENTER_ID = 1L;// 序列号初始值private static long sequence = 0L;// 开始时间戳,用于计算时间戳部分的值(可以使用配置文件读取)private static final long START_TIMESTAMP = 1609459200000L; // 2021/1/1 0:0:0/*** 获取下一个唯一ID* @return 唯一ID*/public synchronized static long getNextId() {long timestamp = System.currentTimeMillis();// 计算时间戳部分的值long timestampPart = (timestamp - START_TIMESTAMP) << 22;// 计算数据中心ID部分的值long dataCenterIdPart = DATA_CENTER_ID << 17;// 计算机器ID部分的值long workerIdPart = WORKER_ID << 12;// 计算序列号部分的值long sequencePart = sequence++ & 0xFFF;if (sequence == 4096L) { // 如果序列号已经达到最大值,重置为0sequence = 0L;}// 组合各部分的值,得到完整的唯一IDreturn timestampPart | dataCenterIdPart | workerIdPart | sequencePart;}public static void main(String[] args) {// 生成10个唯一ID并输出for (int i = 0; i < 10; i++) {System.out.println("第" + (i + 1) + "个唯一ID:" + getNextId());}}
}
第1个唯一ID:113903496345344
第2个唯一ID:113903496345856
第3个唯一ID:113903496346368
第4个唯一ID:113903496346880
第5个唯一ID:113903496347392
第6个唯一ID:113903496347904
第7个唯一ID:113903496348416
第8个唯一ID:113903496348928
第9个唯一ID:113903496349440
第10个唯一ID:113903496349952
以上代码中,通过组合机器ID、数据中心ID、时间戳和序列号等信息生成分布式唯一ID,并使用synchronized
关键字保证线程安全。其中,时间戳部分占用42位(即时间戳-开始时间戳左移22位),数据中心ID部分占用5位,机器ID部分占用5位,序列号部分占用12位。
使用Redis的原子操作INCR或INCRBY命令实现ID的自增,同时保证不重复
import redis.clients.jedis.Jedis;public class RedisDemo {// Redis连接地址(可以使用配置文件读取)private static final String HOST = "localhost";// Redis端口号(可以使用配置文件读取)private static final int PORT = 6379;// Redis密码(如果没有设置密码,则为null)private static final String PASSWORD = null;// Redis键名private static final String KEY_NAME = "id:generator";public static void main(String[] args) {Jedis jedis = null;try {// 建立Redis连接jedis = new Jedis(HOST, PORT);if (PASSWORD != null && !PASSWORD.isEmpty()) { // 如果有密码则进行认证jedis.auth(PASSWORD);}// 初始值设为1jedis.setnx(KEY_NAME, "1");// 执行INCR命令获取下一个IDlong id = jedis.incr(KEY_NAME);System.out.println("生成的唯一ID:" + id);} finally {if (jedis != null) {jedis.close();}}}
}
生成的唯一ID:6
以上代码中,使用Java的redis.clients.jedis.Jedis
库建立与Redis的连接,并通过调用incr()
方法实现ID的自增。其中,setnx()
方法用于初始化键值,将其初始值设为1,并且只在键不存在时才设置它的值。这样可以保证在多个客户端同时连接Redis时不会出现竞争条件。
示例代码:
import java.sql.*;public class DatabaseDemo {// 数据库连接地址(可以使用配置文件读取)private static final String URL = "jdbc:mysql://localhost:3306/test";// 数据库用户名(可以使用配置文件读取)private static final String USERNAME = "root";// 数据库密码(可以使用配置文件读取)private static final String PASSWORD = "password";// 数据库表名private static final String TABLE_NAME = "id_generator";public static void main(String[] args) throws SQLException {Connection conn = null;PreparedStatement stmt = null;ResultSet rs = null;try {// 建立数据库连接conn = DriverManager.getConnection(URL, USERNAME, PASSWORD);// 插入一条记录并获取自增IDString sql = "INSERT INTO " + TABLE_NAME + " (dummy) VALUES (?)";stmt = conn.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS);stmt.setString(1, "dummy value");stmt.executeUpdate();rs = stmt.getGeneratedKeys();if (rs.next()) {long id = rs.getLong(1);System.out.println("生成的唯一ID:" + id);}} finally {if (rs != null) {rs.close();}if (stmt != null) {stmt.close();}if (conn != null) {conn.close();}}}
}
生成的唯一ID:1
以上代码中,使用Java标准库提供的java.sql.*
包建立与MySQL数据库的连接,并通过执行INSERT语句插入一条记录并获取自增ID。其中,Statement.RETURN_GENERATED_KEYS
参数用于指定返回自动生成的主键值,而不是受影响的行数。
Twitter的雪花算法通过记录上次生成ID时的时间戳和序列号,来避免时钟回拨问题。
在正常情况下,每次生成ID时,都会先检查当前时间戳是否小于上次生成ID时的时间戳。如果是,则表示发生了时钟回拨,此时需要等待一段时间以确保时间戳不重复,并尝试生成新的ID。如果等待时间过长或者连续多次出现时钟回拨,则需要抛出异常或者记录日志进行处理。
具体而言,在实现上,记录上次生成ID时的时间戳和序列号需要放在每个节点的本地内存中。当需要生成新的ID时,先获取当前时间戳,然后判断当前时间戳是否小于上次生成ID时的时间戳。如果小于,则表示发生了时钟回拨,此时需要计算时间戳差值并加上序列号,得到一个新的时间戳,然后将序列号重置为0。如果不小于,则直接将序列号加1,并更新上次生成ID时的时间戳。
需要注意的是,为了避免在同一毫秒内生成相同的ID,需要对时钟回拨的情况做特殊处理。具体而言,如果在同一毫秒内发生了时钟回拨,则需要将序列号减去回拨的毫秒数,这样才能确保在同一毫秒内生成的ID不重复。
根据不同的场景和需求,可以选择合适的分布式唯一键生成方案。
上一篇:JavaScript深浅拷贝
下一篇:计算机网络笔记——传输层