Java实现分布式ID案例

wen java案例 2

本文目录导读:

Java实现分布式ID案例

  1. 雪花算法(Snowflake)实现
  2. 基于数据库的ID生成方案
  3. Redis原子操作方案
  4. UUID方案(简单但有缺点)
  5. 完整生产级方案(组合实现)
  6. 使用示例
  7. 最佳实践建议
  8. 方案对比

我将为您提供几个Java实现分布式ID的完整案例,涵盖不同方案。

雪花算法(Snowflake)实现

基础版本

import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.concurrent.atomic.AtomicLong;
public class SnowflakeIdGenerator {
    // 起始时间戳 (2020-01-01)
    private static final long START_TIME = 1577808000000L;
    // 各部分占用的位数
    private static final long SEQUENCE_BITS = 12;   // 序列号占12位
    private static final long MACHINE_ID_BITS = 5;  // 机器ID占5位
    private static final long DATACENTER_ID_BITS = 5; // 数据中心ID占5位
    // 各部分最大值
    private static final long MAX_SEQUENCE = ~(-1L << SEQUENCE_BITS);
    private static final long MAX_MACHINE_ID = ~(-1L << MACHINE_ID_BITS);
    private static final long MAX_DATACENTER_ID = ~(-1L << DATACENTER_ID_BITS);
    // 各部分向左的位移
    private static final long MACHINE_ID_SHIFT = SEQUENCE_BITS;
    private static final long DATACENTER_ID_SHIFT = SEQUENCE_BITS + MACHINE_ID_BITS;
    private static final long TIMESTAMP_SHIFT = SEQUENCE_BITS + MACHINE_ID_BITS + DATACENTER_ID_BITS;
    private final long datacenterId;  // 数据中心ID
    private final long machineId;     // 机器ID
    private long sequence = 0L;       // 序列号
    private long lastTimestamp = -1L; // 上次生成ID的时间戳
    public SnowflakeIdGenerator(long datacenterId, long machineId) {
        if (datacenterId > MAX_DATACENTER_ID || datacenterId < 0) {
            throw new IllegalArgumentException("datacenterId must be between 0 and " + MAX_DATACENTER_ID);
        }
        if (machineId > MAX_MACHINE_ID || machineId < 0) {
            throw new IllegalArgumentException("machineId must be between 0 and " + MAX_MACHINE_ID);
        }
        this.datacenterId = datacenterId;
        this.machineId = machineId;
    }
    public synchronized long nextId() {
        long currentTimestamp = System.currentTimeMillis();
        if (currentTimestamp < lastTimestamp) {
            throw new RuntimeException("Clock moved backwards. Refusing to generate id");
        }
        if (currentTimestamp == lastTimestamp) {
            sequence = (sequence + 1) & MAX_SEQUENCE;
            if (sequence == 0) {
                // 序列号用尽,等待下一毫秒
                currentTimestamp = waitNextMillis(lastTimestamp);
            }
        } else {
            sequence = 0L;
        }
        lastTimestamp = currentTimestamp;
        return ((currentTimestamp - START_TIME) << TIMESTAMP_SHIFT)
                | (datacenterId << DATACENTER_ID_SHIFT)
                | (machineId << MACHINE_ID_SHIFT)
                | sequence;
    }
    private long waitNextMillis(long lastTimestamp) {
        long currentTimestamp = System.currentTimeMillis();
        while (currentTimestamp <= lastTimestamp) {
            currentTimestamp = System.currentTimeMillis();
        }
        return currentTimestamp;
    }
    // 获取本机IP地址生成workerId
    public static long getWorkerId() {
        try {
            InetAddress ip = InetAddress.getLocalHost();
            byte[] ipAddress = ip.getAddress();
            return ((ipAddress[ipAddress.length - 1] & 0xFF) % 32);
        } catch (UnknownHostException e) {
            return Math.abs(java.util.UUID.randomUUID().hashCode()) % 32;
        }
    }
}

使用示例

public class SnowflakeExample {
    public static void main(String[] args) {
        // 创建生成器
        SnowflakeIdGenerator generator = new SnowflakeIdGenerator(1, 1);
        // 生成单个ID
        long id = generator.nextId();
        System.out.println("生成的ID: " + id);
        // 批量生成
        for (int i = 0; i < 10; i++) {
            System.out.println("ID " + (i+1) + ": " + generator.nextId());
        }
        // 并发测试
        ExecutorService executor = Executors.newFixedThreadPool(10);
        Set<Long> ids = ConcurrentHashMap.newKeySet();
        for (int i = 0; i < 1000; i++) {
            executor.submit(() -> {
                long newId = generator.nextId();
                ids.add(newId);
            });
        }
        executor.shutdown();
        try {
            executor.awaitTermination(10, TimeUnit.SECONDS);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("生成的唯一ID数量: " + ids.size());
    }
}

基于数据库的ID生成方案

数据库表结构

CREATE TABLE `distributed_id` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `biz_type` varchar(50) NOT NULL COMMENT '业务类型',
  PRIMARY KEY (`id`),
  UNIQUE KEY `uk_biz_type` (`biz_type`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

Java实现

import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
public class DatabaseIdGenerator {
    private final DataSource dataSource;
    private final int step;  // 步长
    public DatabaseIdGenerator(DataSource dataSource, int step) {
        this.dataSource = dataSource;
        this.step = step;
    }
    public long generateId(String bizType) {
        Connection conn = null;
        PreparedStatement pstmt = null;
        ResultSet rs = null;
        try {
            conn = dataSource.getConnection();
            // 更新并获取新的ID范围
            String sql = "UPDATE distributed_id SET id = LAST_INSERT_ID(id + ?) WHERE biz_type = ?";
            pstmt = conn.prepareStatement(sql);
            pstmt.setInt(1, step);
            pstmt.setString(2, bizType);
            pstmt.executeUpdate();
            // 获取当前ID
            sql = "SELECT LAST_INSERT_ID()";
            pstmt = conn.prepareStatement(sql);
            rs = pstmt.executeQuery();
            if (rs.next()) {
                return rs.getLong(1) - step + 1;
            }
            return -1;
        } catch (SQLException e) {
            throw new RuntimeException("Failed to generate ID", e);
        } finally {
            // 关闭资源
            try {
                if (rs != null) rs.close();
                if (pstmt != null) pstmt.close();
                if (conn != null) conn.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }
}

批量获取IDRange

public class IdRange {
    private final long minId;
    private final long maxId;
    private long currentId;
    public IdRange(long minId, long maxId) {
        this.minId = minId;
        this.maxId = maxId;
        this.currentId = minId;
    }
    public synchronized long nextId() {
        if (currentId > maxId) {
            return -1;  // 范围已用尽
        }
        return currentId++;
    }
    public boolean isExhausted() {
        return currentId > maxId;
    }
    public long getMinId() { return minId; }
    public long getMaxId() { return maxId; }
    public long getCurrentId() { return currentId; }
}

Redis原子操作方案

import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.params.SetParams;
public class RedisIdGenerator {
    private final JedisPool jedisPool;
    private final String keyPrefix;
    private final int step = 100;  // 步长
    public RedisIdGenerator(JedisPool jedisPool, String keyPrefix) {
        this.jedisPool = jedisPool;
        this.keyPrefix = keyPrefix;
    }
    public long generateId(String bizType) {
        String key = keyPrefix + ":" + bizType;
        try (Jedis jedis = jedisPool.getResource()) {
            // 使用INCR命令原子递增
            return jedis.incr(key);
        }
    }
    // 批量获取ID(每批次减少网络开销)
    public IdRange generateIdRange(String bizType) {
        String key = keyPrefix + ":" + bizType;
        try (Jedis jedis = jedisPool.getResource()) {
            long minId = jedis.incrBy(key, step) - step + 1;
            long maxId = minId + step - 1;
            // 初始化缓存中的id范围
            return new IdRange(minId, maxId);
        }
    }
    // 使用Lua脚本保证原子性
    public long generateIdWithLua(String bizType, int count) {
        String key = keyPrefix + ":" + bizType;
        String luaScript = 
            "local current = redis.call('GET', KEYS[1]) " +
            "if not current then current = 0 end " +
            "local next = tonumber(current) + tonumber(ARGV[1]) " +
            "redis.call('SET', KEYS[1], next) " +
            "return next";
        try (Jedis jedis = jedisPool.getResource()) {
            Object result = jedis.eval(luaScript, 1, key, String.valueOf(count));
            return Long.parseLong(result.toString());
        }
    }
}

UUID方案(简单但有缺点)

import java.util.UUID;
public class UUIDGenerator {
    // 标准UUID
    public String generateUUID() {
        return UUID.randomUUID().toString();
    }
    // 去除连字符的UUID
    public String generateUUIDWithoutDash() {
        return UUID.randomUUID().toString().replace("-", "");
    }
    // 数字格式的UUID(适合作为Long类型ID)
    public Long generateNumericId() {
        return Math.abs(UUID.randomUUID().getMostSignificantBits());
    }
    // 转化为Base64缩短长度
    public String generateShortUUID() {
        UUID uuid = UUID.randomUUID();
        byte[] bytes = new byte[16];
        long most = uuid.getMostSignificantBits();
        long least = uuid.getLeastSignificantBits();
        for (int i = 0; i < 8; i++) {
            bytes[i] = (byte) (most >>> (56 - i * 8));
            bytes[8 + i] = (byte) (least >>> (56 - i * 8));
        }
        return java.util.Base64.getUrlEncoder().withoutPadding().encodeToString(bytes);
    }
}

完整生产级方案(组合实现)

import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicLong;
public class DistributedIdGenerator {
    // 配置
    private static class IdConfig {
        final long datacenterId;
        final long workerId;
        final long timestampBits = 41;
        final long datacenterBits = 5;
        final long workerBits = 5;
        final long sequenceBits = 12;
        final long maxSequence = ~(-1L << sequenceBits);
        final long maxWorker = ~(-1L << workerBits);
        final long maxDatacenter = ~(-1L << datacenterBits);
        final long workerShift = sequenceBits;
        final long datacenterShift = sequenceBits + workerBits;
        final long timestampShift = sequenceBits + workerBits + datacenterBits;
        long startTimestamp = 1577808000000L;  // 2020-01-01
        final long maxTimestamp = ~(-1L << timestampBits) + startTimestamp;
        IdConfig(long datacenterId, long workerId) {
            if (datacenterId > maxDatacenter || datacenterId < 0) {
                throw new IllegalArgumentException("Invalid datacenterId");
            }
            if (workerId > maxWorker || workerId < 0) {
                throw new IllegalArgumentException("Invalid workerId");
            }
            this.datacenterId = datacenterId;
            this.workerId = workerId;
        }
    }
    // 各种生成器实例
    private final SnowflakeIdGenerator snowflakeGenerator;
    private final RedisIdGenerator redisGenerator;
    private final DatabaseIdGenerator databaseGenerator;
    private final UUIDGenerator uuidGenerator;
    // 缓存用于当前ID生成策略
    private final ConcurrentHashMap<String, AtomicLong> idCache = new ConcurrentHashMap<>();
    public DistributedIdGenerator() {
        // 初始化各生成器
        this.snowflakeGenerator = new SnowflakeIdGenerator(1, 1);
        this.redisGenerator = null;  // 需要配置Redis连接池
        this.databaseGenerator = null;  // 需要配置数据源
        this.uuidGenerator = new UUIDGenerator();
    }
    // 根据不同的业务场景选择合适的ID生成策略
    public long generateSnowflakeId() {
        return snowflakeGenerator.nextId();
    }
    public String generateUUID() {
        return uuidGenerator.generateUUIDWithoutDash();
    }
    // 带缓存策略的ID生成
    public long generateCachedId(String bizType) {
        AtomicLong cache = idCache.computeIfAbsent(bizType, k -> new AtomicLong(0));
        long id = cache.getAndIncrement();
        // 如果缓存中的ID不够用,从数据库获取新的范围
        if (id >= 1000000) {  // 假设每100万需要刷新
            // 从数据库获取新的ID范围
            // idCache.put(bizType, new AtomicLong(newRangeStart));
        }
        return id;
    }
}

使用示例

public class DistributedIdDemo {
    public static void main(String[] args) {
        // 1. 雪花算法
        SnowflakeIdGenerator snowflake = new SnowflakeIdGenerator(1, 1);
        System.out.println("雪花算法 ID: " + snowflake.nextId());
        // 2. UUID
        UUIDGenerator uuidGen = new UUIDGenerator();
        System.out.println("UUID: " + uuidGen.generateUUIDWithoutDash());
        // 3. 并发测试
        ConcurrentHashMap<Long, Boolean> idSet = new ConcurrentHashMap<>();
        ExecutorService executor = Executors.newFixedThreadPool(20);
        long startTime = System.currentTimeMillis();
        // 提交10000个任务
        CountDownLatch latch = new CountDownLatch(10000);
        for (int i = 0; i < 10000; i++) {
            executor.submit(() -> {
                long id = snowflake.nextId();
                idSet.put(id, true);
                latch.countDown();
            });
        }
        try {
            latch.await(10, TimeUnit.SECONDS);
            long endTime = System.currentTimeMillis();
            System.out.println("生成10000个唯一ID数量: " + idSet.size());
            System.out.println("耗时: " + (endTime - startTime) + "ms");
            System.out.println("平均耗时: " + (startTime - endTime) / 10000.0 + "ms/个");
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            executor.shutdown();
        }
    }
}

最佳实践建议

public class IdGeneratorFactory {
    // 根据业务需求选择合适的ID生成策略
    public static IdGeneratorStrategy createStrategy(IdType type, IdConfig config) {
        switch (type) {
            case SNOWFLAKE:
                return new SnowflakeStrategy(config);
            case REDIS:
                return new RedisStrategy(config);
            case DATABASE:
                return new DatabaseStrategy(config);
            case MIXED:
                return new MixedStrategy(config);
            default:
                throw new IllegalArgumentException("Unsupported ID type");
        }
    }
    enum IdType {
        SNOWFLAKE,    // 雪花算法
        REDIS,        // Redis生成
        DATABASE,     // 数据库生成
        MIXED         // 混合策略
    }
    interface IdGeneratorStrategy {
        long nextId(String bizType);
        String nextIdString(String bizType);
    }
    static class SnowflakeStrategy implements IdGeneratorStrategy {
        private final SnowflakeIdGenerator generator;
        SnowflakeStrategy(IdConfig config) {
            this.generator = new SnowflakeIdGenerator(config.datacenterId, config.workerId);
        }
        @Override
        public long nextId(String bizType) {
            return generator.nextId();
        }
        @Override
        public String nextIdString(String bizType) {
            return String.valueOf(generator.nextId());
        }
    }
    // 其他策略实现...
}

方案对比

方案 优点 缺点 适用场景
雪花算法 高性能、趋势递增、无网络依赖 依赖机器时钟 高并发分布式环境
数据库 简单、可靠 性能瓶颈、需维护 小规模系统
Redis 高性能、灵活 依赖Redis、持久化问题 中高并发系统
UUID 简单、全局唯一 无序、存储浪费 非索引字段

选择合适的方案需要根据业务场景、并发量、系统架构等因素综合考虑,建议在实际生产环境中结合多种方案使用。

抱歉,评论功能暂时关闭!