暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

JAVA:生成唯一标识的技术指南

拾荒的小海螺 2024-09-18
582

1、简述

在 Java 中生成唯一标识符(UUID)是开发过程中常见的需求,特别是在分布式系统或需要确保数据唯一性的场景中。Java 提供了多种方法生成唯一标识符,常用的方式包括使用 UUID 类、Snowflake 算法以及自定义生成逻辑。下面我将介绍这些方法,并为每种方法提供代码示例。


2、使用 UUID 类生成唯一标识符

Java 标准库中的 UUID 类可以轻松生成唯一标识符。UUID 是基于随机数的通用唯一识别码(Universally Unique Identifier),它生成的是 128 位长的数字,通常以 36 个字符(包括 4 个连字符)表示。

    import java.util.UUID;


    public class UUIDExample {
    public static void main(String[] args) {
    // 生成一个随机 UUID
    UUID uuid = UUID.randomUUID();
    System.out.println("生成的 UUID: " + uuid.toString());
    }
    }

    特点:

    优点:简单易用,生成速度快。

    缺点:不能保证 UUID 的有序性,在高并发场景下可能导致性能问题。


    3、基于 Snowflake 算法生成唯一标识符

    Snowflake 是 Twitter 开源的分布式唯一 ID 生成算法,生成的 ID 是 64 位的数字。它的特点是高效且有序,特别适用于分布式系统中需要生成全局唯一且时间有序的 ID。

      public class SnowflakeIdGenerator {
      private final long workerId;
      private final long datacenterId;
      private final long sequence = 0L;

      private final long twepoch = 1288834974657L;
      private final long workerIdBits = 5L;
      private final long datacenterIdBits = 5L;
      private final long maxWorkerId = -1L ^ (-1L << workerIdBits);
      private final long maxDatacenterId = -1L ^ (-1L << datacenterIdBits);
      private final long sequenceBits = 12L;

      private final long workerIdShift = sequenceBits;
      private final long datacenterIdShift = sequenceBits + workerIdBits;
      private final long timestampLeftShift = sequenceBits + workerIdBits + datacenterIdBits;
      private final long sequenceMask = -1L ^ (-1L << sequenceBits);

      private long lastTimestamp = -1L;
      private long sequenceNum = 0L;

      public SnowflakeIdGenerator(long workerId, long datacenterId) {
      if (workerId > maxWorkerId || workerId < 0) {
      throw new IllegalArgumentException("Worker ID out of range");
      }
      if (datacenterId > maxDatacenterId || datacenterId < 0) {
      throw new IllegalArgumentException("Datacenter ID out of range");
      }
      this.workerId = workerId;
      this.datacenterId = datacenterId;
      }

      public synchronized long nextId() {
      long timestamp = timeGen();

      if (timestamp < lastTimestamp) {
      throw new RuntimeException("Clock moved backwards");
      }

      if (lastTimestamp == timestamp) {
      sequenceNum = (sequenceNum + 1) & sequenceMask;
      if (sequenceNum == 0) {
      timestamp = tilNextMillis(lastTimestamp);
      }
      } else {
      sequenceNum = 0L;
      }

      lastTimestamp = timestamp;
      return ((timestamp - twepoch) << timestampLeftShift)
      | (datacenterId << datacenterIdShift)
      | (workerId << workerIdShift)
      | sequenceNum;
      }

      private long tilNextMillis(long lastTimestamp) {
      long timestamp = timeGen();
      while (timestamp <= lastTimestamp) {
      timestamp = timeGen();
      }
      return timestamp;
      }

      private long timeGen() {
      return System.currentTimeMillis();
      }

      public static void main(String[] args) {
      SnowflakeIdGenerator idGenerator = new SnowflakeIdGenerator(1, 1);
      for (int i = 0; i < 10; i++) {
      System.out.println(idGenerator.nextId());
      }
      }
      }

      特点:

      优点:生成的 ID 有序性好,适合分布式系统。

      缺点:实现稍复杂,需要考虑时钟回拨等问题。


      4、自定义时间戳加随机数生成唯一标识符

      如果项目中对唯一标识的规则有特别的需求,可以根据业务逻辑来自定义生成策略。例如,使用当前时间戳和随机数的组合来生成唯一标识符。

        import java.util.Random;


        public class CustomIdGenerator {
        public static String generateId() {
        long timestamp = System.currentTimeMillis();
        int randomNum = new Random().nextInt(10000);
        return timestamp + String.format("%04d", randomNum);
        }


        public static void main(String[] args) {
        for (int i = 0; i < 5; i++) {
        System.out.println("生成的自定义 ID: " + generateId());
        }
        }
        }

        特点:

        优点:可以根据具体业务需求进行定制。

        缺点:需要自己设计生成逻辑和保证唯一性。


        5、基于数据库的分布式唯一 ID 生成

        通过数据库的自增字段来生成唯一标识符是最简单的一种方式,适用于小规模应用场景。可以利用 MySQL、PostgreSQL 等数据库的自增 ID 功能,生成唯一标识符。

          CREATE TABLE orders (
          id BIGINT AUTO_INCREMENT PRIMARY KEY,
          order_name VARCHAR(255) NOT NULL
          );


          在插入数据时,数据库会自动生成唯一的自增 ID。

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


            public class DatabaseIdGenerator {
            public static void main(String[] args) throws Exception {
            Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/test", "root", "password");
            PreparedStatement stmt = conn.prepareStatement("INSERT INTO orders (order_name) VALUES (?)", PreparedStatement.RETURN_GENERATED_KEYS);
            stmt.setString(1, "order_1");
            stmt.executeUpdate();


            ResultSet rs = stmt.getGeneratedKeys();
            if (rs.next()) {
            long generatedId = rs.getLong(1);
            System.out.println("生成的数据库自增 ID: " + generatedId);
            }
            conn.close();
            }
            }

            特点:

            优点:实现简单,适合小规模应用。

            缺点:扩展性差,数据库压力较大,不适合高并发环境。


            6、基于 Redis 的分布式唯一 ID 生成

            通过 Redis 自增功能生成唯一标识符也是一种常见的方法,特别适用于高并发场景。Redis 的自增操作是原子性的,因此非常适合分布式环境中生成全局唯一 ID。

              import redis.clients.jedis.Jedis;


              public class RedisIdGenerator {
              private static Jedis jedis = new Jedis("localhost");


              public static String generateId(String key) {
              long id = jedis.incr(key); // 使用 Redis 自增生成 ID
              return String.valueOf(id);
              }


              public static void main(String[] args) {
              for (int i = 0; i < 5; i++) {
              System.out.println("生成的 Redis ID: " + generateId("order_id"));
              }
              }
              }

              特点:

              优点:Redis 的自增操作效率高,适合高并发场景。

              缺点:需要维护 Redis 集群,并且 Redis 宕机时需要考虑备份机制。


              7、美团的 Leaf 分布式 ID 生成器

              美团的 Leaf 是一个高可用、低延迟的分布式 ID 生成系统,支持两种模式:号段模式(Segment Model) 和 雪花算法模式(Snowflake Model)。Leaf 通过号段模式为每个业务预分配一段 ID,提高生成效率。

              号段模式工作原理:

              Leaf 将一段连续的 ID 预分配给应用,避免每次生成 ID 都与数据库交互,减小数据库的压力。

              每次只需要更新号段的最大值,可以实现高并发的 ID 生成。


              引入 Leaf 的示例:

              首先,需要在项目中集成 Leaf,通常通过引入 Leaf 的服务。Leaf 由两部分组成:Leaf Server 和 Leaf Core,Server 是提供 ID 生成服务的接口。假设使用 Maven 构建项目,首先需要引入 Leaf 的依赖:

                <dependency>
                <groupId>com.sankuai.inf.leaf</groupId>
                <artifactId>leaf-core</artifactId>
                <version>1.0.2</version>
                </dependency>


                号段模式生成 ID:

                  import com.sankuai.inf.leaf.common.Result;
                  import com.sankuai.inf.leaf.service.SegmentService;
                  import com.sankuai.inf.leaf.service.impl.SegmentServiceImpl;


                  public class LeafSegmentExample {
                  public static void main(String[] args) {
                  // 初始化 SegmentService 实例,通常通过 Spring 容器管理
                  SegmentService segmentService = new SegmentServiceImpl();


                  // 生成唯一标识符
                  Result result = segmentService.getId("order_id");
                  if (result.getStatus() == Result.Status.SUCCESS) {
                  System.out.println("生成的 ID: " + result.getId());
                  } else {
                  System.err.println("ID 生成失败");
                  }
                  }
                  }


                  特点:

                  优点:支持高并发,减少数据库操作,号段提前分配。

                  缺点:需要部署 Leaf 服务,维护相对复杂。


                  8、 百度的 UIDGenerator 分布式 ID 生成器

                  百度的 UIDGenerator 是基于雪花算法(Snowflake)的分布式 ID 生成方案,主要优化了时钟回拨的问题,并且支持配置不同的时间位和节点位,满足不同的业务场景。

                  UIDGenerator 工作原理:

                  UID 由时间戳、工作机器号、序列号等部分组成。

                  它通过多种手段减少时钟回拨带来的问题,且生成的 ID 是递增的。


                  引入 UIDGenerator 的示例:

                  首先,引入 UIDGenerator 的依赖:

                    <dependency>
                    <groupId>com.baidu.fsg</groupId>
                    <artifactId>uid-generator</artifactId>
                    <version>1.0.0</version>
                    </dependency>


                    UIDGenerator 示例代码:

                      import com.baidu.fsg.uid.impl.CachedUidGenerator;
                      import org.springframework.beans.factory.annotation.Autowired;


                      public class UIDGeneratorExample {
                      @Autowired
                      private CachedUidGenerator uidGenerator;


                      public void generateUID() {
                      long uid = uidGenerator.getUID();
                      System.out.println("生成的 UID: " + uid);
                      }
                      }

                      特点:

                      优点:高性能,优化了时钟回拨问题,ID 生成速度快。

                      缺点:依赖性强,需要定制化配置,适合大规模分布式场景。


                      9、总结

                      在 Java 中生成唯一标识符有多种方式可选,开发者可以根据项目的需求选择合适的生成方法:

                      如果只是简单地需要唯一标识,UUID 是最快捷的选择。

                      在分布式系统中需要有序性且高性能的 ID 生成时,Snowflake 是推荐的选择。

                      如果有定制化需求,可以通过组合时间戳、随机数等生成自定义唯一标识符。

                      这篇博客可以结合项目场景讨论各个方法的优缺点,并附带相应的代码示例和输出。


                      文章转载自拾荒的小海螺,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

                      评论