流量不大的情況下,訂單號生成
很久之前寫過一篇利用DB生成業務主鍵的文章,介紹了利用DB來生成唯一的ID。當時便是用這種方式來生成訂單號的。只不過拿到ID後,根據訂單業務,簡單加個前綴而已。
@Service
public class KeyGen{
@Autowired
private KeyGenRepository keyGenRepository;
public long genNo(){
KeyGen keyGen = new KeyGen();
keyGenRepository.genarateNo(keyGen );
//這個就是我們需要的no
long no = keyGen.getNo();
}
}
private String generateOrderNo() {
StringBuffer sbf=new StringBuffer();
//前綴
sbf.append("100");
long no = keyGen.genNo();
sbf.append(no);
return sbf.toString();
}
這種方式用了一段時間,沒發現有訂單號重複的情況。這種解決方案算是一個基礎的思路
,再複雜的訂單生成規則,如果訂單號要包含一個唯一的屬性,利用數據庫的自增特性是個不錯的方案。
大流量下訂單號的生成
如果每個小時的訂單量非常大,比如說,一個小時有兩百萬個訂單,只用單獨一個key_gen
表是支持不住的,畢竟寫入的壓力太大了,影響訂單號的生成速度。這個時候可以考慮針對訂單號的生成,搞單獨的
庫,並分庫,降低insert
的壓力,提高生成訂單號的速度。
分庫的規則
有一種做法是根據倉庫來映射,比如說,一家電商公司的倉庫總共有50個,那麼可以進行如下的映射:
warehouse1:數據庫0
warehouse2:數據庫0
warehouse3:數據庫1
warehouse4:數據庫1
warehouse5:數據庫2
warehouse6:數據庫2
warehouse7:數據庫3
warehouse8:數據庫3
warehouse9:數據庫3
warehouse10:數據庫4
。。。。。。
。。。。。。
warehouse50:數據庫9
將50個倉庫映射到【0-9】對應的數據庫上,當下單的時候,如果訂單對應的倉庫的是warehouse1,則映射到數據庫0,對應的倉庫如果是warehouse10,則對應的數據庫4。這樣子,訂單號的生成的壓力便分配到10個數據庫上了。
進行分庫後,每個分庫裏都有一張key_gen
表。
組裝訂單號
上面的分庫分表,目的是爲了生成一個唯一的ID,這個ID是訂單號的一部分,生成ID藉助了數據庫,但是後面組裝訂單號則完全是業務邏輯操作,無需利用數據庫了。
訂單號的生成規則各個公司都有自己的要求,舉個例子:
時間 + 6位隨機數 + 數據庫生成的唯一ID+倉庫標識
時間的生成可以簡單的使用如下代碼生成:
SimpleDateFormat formatShort = new SimpleDateFormat("yyMMdd");
Date now = new Date();
String currentDate = formatShort.format(now);
六位的隨機數可以藉助JAVA的AtomicLong
來實現,可以應付併發。
到此訂單號完整的生成了。那有沒有坑呢?因爲進行了分庫,每個庫都有key_gen
,生成的ID只是庫內的唯一,多個庫則是未必的。比如說,兩個訂單創建的請求,倉庫分別是warehouse1和warehouse3,根據上面的配置規則,分別路由到了數據庫0
和數據庫1
這兩個庫,這個時候,就可能產生相同的ID。但是不要忘記,訂單號的生成是包含倉庫標識的,一個1,一個是3,是不同的,另外還有隨機數,所以訂單號重複的機會基本不太可能的。
提供批量生成訂單號的接口
尤其是將生成訂單號定義爲一個微服務接口的,一定要提供批量生成訂單號的接口,在流量很大的情況下,每獲取一個訂單號就要走一次網絡調用,開銷實在太大了。那麼一次要生成多少個才合適呢,這個根據自己的業務情況,一般情況下20-50個是夠用的。