1、基于时间戳生成流水号
利用当前时间戳生成流水号,可以确保唯一性。通过格式化时间戳,可以生成固定位数的流水号。
SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmssSSS");
String serialNumber = sdf.format(new Date());
特点:时间戳生成的流水号具有唯一性和顺序性,适合高并发场景。
优点:无需额外存储状态,实现简单。
缺点:流水号长度可能不固定,需要根据需求调整格式。
2、使用数据库自增ID
通过数据库的自增主键生成流水号,可以保证唯一性和连续性。
// 假设使用JPA
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
特点:依赖数据库的自增机制,适合有数据库支持的场景。
优点:实现简单,保证唯一性和连续性。
缺点:需要数据库支持,性能可能受数据库限制。
3、基于Redis生成流水号
利用Redis的原子性操作生成自增流水号,适合分布式系统。
// 使用RedisTemplate
Long serialNumber = redisTemplate.opsForValue().increment("serial_key");
特点:Redis的INCR命令是原子性的,适合高并发场景。
优点:性能高,支持分布式系统。
缺点:需要引入Redis,增加系统复杂性。
4、UUID生成流水号
使用UUID生成唯一标识符,可以保证全局唯一性。
String serialNumber = UUID.randomUUID().toString().replace("-", "");
特点:UUID生成的流水号长度固定,但无序。
优点:无需中央协调器,适合分布式系统。
缺点:流水号较长且无序,不适合需要顺序的场景。
5、雪花算法生成流水号
雪花算法(Snowflake)生成的ID具有时间有序性,适合分布式系统。
// 使用Hutool工具类
long serialNumber = IdUtil.getSnowflake(1, 1).nextId();
特点:结合时间戳、机器ID和序列号生成ID,保证唯一性和顺序性。
优点:性能高,适合分布式系统。
缺点:依赖系统时钟,时钟回拨可能导致ID重复。
6、自定义计数器生成流水号
通过内存中的计数器生成流水号,适合单机或低并发场景。
private static AtomicLong counter = new AtomicLong(0);
String serialNumber = String.format("%010d", counter.incrementAndGet());
特点:实现简单,适合单机或低并发场景。
优点:无需外部依赖,性能高。
缺点:重启后计数器会重置,不适合分布式系统。
7、结合日期和序列号生成流水号
将当前日期与序列号结合生成流水号,适合需要日期前缀的场景。
SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd");
String dateStr = sdf.format(new Date());
String serialNumber = dateStr + String.format("%04d", sequence.incrementAndGet());
特点:流水号包含日期信息,便于分类和查询。
优点:实现简单,适合需要日期前缀的场景。
缺点:序列号每日重置,可能重复。
8、使用ZooKeeper生成流水号
利用ZooKeeper的顺序节点生成唯一且有序的流水号。
// 使用CuratorFramework
String path = curatorFramework.create().withMode(CreateMode.PERSISTENT_SEQUENTIAL).forPath("/serial/");
String serialNumber = path.substring(path.lastIndexOf('/') + 1);
特点:ZooKeeper的顺序节点保证唯一性和顺序性。
优点:适合分布式系统,保证高可用性。
缺点:依赖ZooKeeper,性能较低。