对这种需求,第一印象是随机数,如果跟系统冲突则+1。但是对于不确定的业务量,这样做容易在达到上限时频繁冲突。当然可以判断冲突n次后报错,那就不太稳定。
灵光一现的觉得用散列哈希可以搞定,结果不行。
后来发现最稳妥的办法就是用洗牌算法提前生成好随机序列存到redis,用的时候lpop就行。一是洗牌算法效率很高,二是纯数字不是很占用空间。可行性是ok的。
然后就是redis数据丢失问题。如何恢复随机序列呢?
我做了个实验,通过相同seed的Random对象,洗牌得到的随机序列是相同的:
public static void main(String[] args) {
final long epochDay = LocalDate.now().toEpochDay();
System.out.println(epochDay);
List<Integer> l = new ArrayList<>();
for (int i = 0; i < 100; i++) {
l.add(i);
}
Collections.shuffle(l,new Random(epochDay));
System.out.println(l);
l = new ArrayList<>();
for (int i = 0; i < 100; i++) {
l.add(i);
}
Collections.shuffle(l,new Random(epochDay));
System.out.println(l);
}
于是就可以这样恢复:
要获取序列号时,发现redis中序列丢失,立即加锁。利用当天日期作为seed重新生成序列后,在业务表找到创建时间最晚的编号:如果当天没有序号则拿第一个返回;如果有,则循环remove序列到与该编号相同的位置,拿到下一个返回。剩下的序列放回redis中。
流程图如下: