优秀的流水号, 应该是唯一的/ 可读的/ 灵活的/ 高效的.
常见的流水号生成方式
- 时间戳 Java8以上支持纳秒了 1 秒钟等于 10 的 9 次方纳秒. 很完美. 但是理论上唯一性不足. 如果加上机器id, 管理又比较麻烦.
1 2 3 4 5 6 7 8
| @Test public void test03() { Date now = new Date(); SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS"); String formattedDate = sdf.format(now); long nano1 = System.nanoTime() % 10000000000L; System.out.println(formattedDate + "." + String.format("%05d", nano1)); }
|
- 通过数据库记录. 每次+n. 加号和查询必须加事务. 不然会导致重号.多应用同时取号, 并发时都存在瓶颈. 高效性不行.
- uuid 丑+长
- 雪花算法 不可读 如果加上时间和类型又太长了
https://www.junjun.fun/23_04_25_serial_number_gen_time_randon
- 因此, 都不太妙. 考虑到分布式取号情况, 只能找个介质把当前号存起来. 数据库已经排除了.
- redisTemplate.opsForValue().increment可以自动返回增加后的值, 不存在并发加事务的情况, 非常合适.
基于Redis的流水号生成方案
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
| @Configuration public class RedisGen {
@Resource public RedisTemplate redisTemplate;
public String getId(String key, Long delta) { try { if (null == delta) { delta = 1L; } String timeStamp = new SimpleDateFormat("yyMMddHHmmss").format(new Date()); String newKey = key + ":" + timeStamp; Long increment = redisTemplate.opsForValue().increment(newKey, delta); redisTemplate.expire(newKey, 2, TimeUnit.SECONDS); if (increment.equals(10000L)) { return getId(key, null); } return timeStamp + String.format("%04d", increment); } catch (Exception e) { String timeStamp = new SimpleDateFormat("yyMMddHHmmssSSS").format(new Date()); Random random = new Random(); timeStamp += (random.nextInt(10) + ""); return timeStamp; } } }
|