服務器開發——訂單號生成策略

訂單是整個電子商務的核心,整個電子商務的流程也是圍繞訂單展開的。本文與大家分享一下各大電子商務網站訂單號的生成方式。

訂單號

概念:它是您在購物網站購物後獲得的訂單號,記錄的是購物訂單信息。

作用:在您需要與購物網站進行訂單查詢等操作時,需要給購物網站提供商家訂單號。

下單途徑:

  • web網站下單
  • 打電話到呼叫中心(CallCenter)下單
  • 手機wap下單

如果採用單數據庫來存儲的話,隨着訂單量的增加,單庫的寫壓力增大,造成數據庫服務器性能下降。一般會採用分庫來緩解數據庫服務器的壓力。

進行分庫:

  • web來源訂單,存入web訂單庫。
  • CallCenter來源訂單,存入CallCenter訂單庫。
  • wap來源訂單,存入wap訂單庫。

最終,將這三種類型的數據庫同步到訂單主庫中。

把不同的訂單同步到訂單主庫:電商網站一般利用訂單號來作爲訂單表的主鍵。因此,我們必須保證訂單號不重複,才能將訂單安全的同步到訂單主庫中。

訂單命名規定

唯一性:保證訂單號不重複。

安全性:訂單編號不能透露你公司的真實運營信息,比如你的訂單就是流水號的話,那麼別人就可以從訂單號推測出你公司的整體運營概括了。所以訂單編碼必須是除了你們公司少部分人外,其他人基本看不懂的。可以參考京東和淘寶的編碼規則。

不能使用大規模隨機碼:因爲大規模的隨機碼隨機生成,因爲本身就沒有意義所以無所謂泄密了。但是事實上這種編碼規則在實現上會有很大問題的。隨機碼滿足第二點安全性要求,爲了滿足唯一性,那就得在生成隨機碼的時候對比歷史數據是否有重複,如果你的訂單數量到達了十萬次,你每次生成訂單編碼時就得對比十萬條歷史數據。

隨機碼就不能在編碼中使用了嗎?小規模的隨機碼是可以使用的,比如2~3位,這種隨機碼一般都是和流水號等結合使用,主要作用是爲了隱藏流水號的真實數據而進行使用的。

防止併發:主要針對編碼中有時間的設定。

控制位數:訂單號的作用就是便於查詢。一般正常使用場景應該是訂單出異狀或者退貨的時候,用戶將訂單號報給客服,由客服進行查詢。所以一般在10~15位爲好。目前京東11位,淘寶16位。

怎麼保證訂單號的唯一性

訂單號命名規則來生成

如“業務編碼 + 時間戳 + 機器編號[前4位] + 隨機4位數 + 毫秒數”。

說明:業務編碼(OrderType: Web=1 CallCenter=2 Wap=3) 機器編號(用來表示由那臺服務器生成的訂單)

僞代碼如下:

import org.apache.commons.lang3.RandomStringUtils;

public static String genOrderNo(OrderType type){
    Date curDate = new Date();
    String orderType = getType(type);
    String dateStr4yyyyMMddHHmmss = new SimpleDateFormat("yyyyMMddHHmmss").format(curDate);
    String dateStr4SSS = new SimpleDateFormat("SSS").format(curDate);
    String random = RandomStringUtils.random(4,false,true);
    String machineCode = getMachineCode().substring(0,4);
    return orderType+dateStr4yyyyMMddHHmmss+machineCode+random+dateStr4SSS;
}

缺點:這種方式在高併發下會頻繁更新訂單量記錄表,很容易產生鎖表。但是鎖表問題也是可以解決的,加一層緩存。

全局訂單號數據池來生成

數據庫創建一個訂單號數據庫表(order_id_generator);利用python腳本生成一批訂單號,將這批訂單號存入到order_id_generator表中。生成訂單時,會首先調用事務GET_ORDER_ID_FOR_REGISTER,獲得當前訂單號,再生成訂單。這樣就保證了子庫訂單號不會重複。

數據庫代碼如下:

CREATE TABLE `order_id_generator` (
  `RANDOM_VALUE` bigint(20) NOT NULL,
  `ORDER_ID` int(11) NOT NULL,
  PRIMARY KEY (`RANDOM_VALUE`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

DROP PROCEDURE IF EXISTS `GET_ORDER_ID_FOR_REGISTER`;
DELIMITER ;;
CREATE DEFINER=`root`@`%` PROCEDURE `GET_ORDER_ID_FOR_REGISTER`()
BEGIN
    declare userMid int default 0; 
    declare randomValue bigint default 0;
    start transaction;
        select ORDER_ID, RANDOM_VALUE into orderId, randomValue from order_id_generator limit 1 for update;
            if userMid > 0 then
                delete from order_id_generator where RANDOM_VALUE = randomValue;
            end if;
    commit;

    select orderId from dual;
END
;;
DELIMITER ;

僞代碼如下:

Long getIdForOrder();

總結

訂單號的生成方案,需要根據目前的訂單量而定;因爲各種方案都有各自的使用場景。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章