mysql自動生成訂單號
① 如何保證生成的訂單號的唯一性
訂單號不能採用時間戳的形式,因為不可能精確到秒,那樣訂單號過長 這種情況下只能在資料庫這端進行控制,但是也沒有看到資料庫有類似oracle的序列 問題補充:如果每次都去資料庫中查詢的話效率就有問題了 問題補充:guoxu0514 寫道擔心效率問題的話,你可以考慮用緩存來解決,不會有什麼事兒都有兩全其美的解決辦法。緩存也有問題,比如無法保證集群環境下訂單號的唯一性 問題補充:nixiangyan 寫道直接交個資料庫管理不久行了嗎這種情況下只能在資料庫這端進行控制,但是也沒有看到資料庫有類似oracle的序列 如果將用過的訂單號插到資料庫中,每次取最大的,效率上會不會有問題 問題補充:nixiangyan 寫道其實 我感覺吧,效率是相對的,當你提出某種要求的時候實質上很大的可能是在犧牲效率,交個資料庫管理如果是自增的話,你可能會說訂單號太單一,如果使用時間你說太長,還有使用時間為什麼不能精確到秒?可以解釋一下嗎? 我感覺做軟體不能太在意效率了,呵呵,主要是客戶給一定的規則,精確到秒的話訂單太長了,效率問題倒是可以先不考慮 問題補充:hu437 寫道可以這樣做的,取最大的,不一定要是從源表裡面取的,比如現在有一個訂單表,我再加一個計數表 當每生成一個訂單,我就同時將這個最大的訂單號存到計數表裡面,計數表裡面可以只有兩個欄位,一個叫計數內容,一個叫計數值
② 我正在做一個餐飲系統,碰到了生成唯一訂單號的問題。我想通過資料庫自增 ID ,把自增ID作為訂單號
一般是取機號、年,月,日加時間來區分的,自加ID,過一段時音就不易對帳了
③ 怎麼隨機生成的Id變成16位的訂單號
關於生成訂單號的解決方案
電子商務及類電子商務的系統越來越多,我相信訂單號問題是這類系統中最常見不過的一個問題了,但今天還是想談談。
這幾天由於工作需要接手了另外一同事前期開發的一個交易系統,原本使用的是uniqid()函數生成的。uniqid()是根據系統時間經過一定演算法得到的一個結果,關於uniqid()的詳情手冊上很清楚。
當時的生產方式是:
$order_sn = str_replace('.', '', uniqid('', true));
這種方式理論上說會重復,但是在實際應用中我相信這種重復可以認為是不可能事件。但是,如果這件事情到此就結束的話我也就不會再寫這篇文章。這幾天做支付接入,國內某大型網路支付機構只支持傳遞最多16位的訂單號,無奈我只得調整訂單號的生產規則。
其實關於生成訂單號的方式非常多,大致有以下幾個參數被用到:1、自增欄位,2、系統時間,3、隨機數,4、流水號。
一、資料庫自增欄位
二、簡單的使用系統時間
三、系統時間加隨機數
四、系統時間加流水號
先說說資料庫自增欄位,這種方式是最簡單有效的方式,但同時也存在很大的弊端:
1、以mysql為例的int類型最多存儲10位的數字,如果使用bigint則在使用php的mysql_insert_id()取上次插入id時會出現錯誤,當然這個錯誤是可以採用某些方法避免的。
2、很多時候業務邏輯需要在數據未插入系統之前就獲得訂單號以進行一系列的處理,這樣就容易出錯。比如當並發較高的時候系統獲取到下一次插入的ID應該是10000,可是當真正insert的時候發現10000已經被其他插入行使用。
3、很容易透露出系統的銷量,從商業層面說這種方式不太合適。
4、表現不夠直觀,不能通過訂單號簡表達訂單信息
簡單的使用系統時間也可以有多種方式比如直接使用time()生成10位數字,這種方式基本避免了資料庫自增欄位的大部分弊端,但同時也產生的一些新的問題,比如:並發量稍高(峰值每秒一次以上,相信這是個很小的值)就會產生相同訂單號,而這是業務邏輯所不允許的。為了解決重復訂單問題而使用隨機數或者流水號。
先說隨機數,這東西就跟看上去的字面意思一樣,總顯得不那麼可靠,我認為盡量不使用它參與唯一標識。
再說流水號,既然叫流水號,它的性質其實和自增欄位一樣,不同的是或許每天或者每月流水號又會重新計數。總得有個地方來保存下一個(或者當前使用過的最大)流水號的值,如果存在文件中那就需要考慮這個文件的讀寫鎖的問題,就這個問題估計足夠寫書了,在此不予討論。如果以自增方式存在DB中,那麼我們在程序生成訂單號之前需要多訪問(至少)一次DB,這也就降低了程序性能,要知道資料庫訪問對程序的性能影響是非常明顯的
上面是一大堆廢話,說說我的解決思路(PHP),當然同時別忘了大前提是:限制長度16位
第一步:
$order_sn = date('ymdHis').substr(microtime(),2,4);
其實這種方式基本已經滿足需求了,無需訪問DB無隨機數參與。但是如果兩次請求在相同的十萬分之一秒內產生,那麼相同訂單就產生了,看能否有辦法繼續提高。
date(『His』)所表達的結果其實就是000000到235959,而且其中很多數字不會被用到比如126998。一天86400秒,如果從一天的0:0:0算起直到23:59:59使用00000-86400就可以完全表示,這樣下來我們就完全可以把date(『His』)換成五位數字。既然time()函數就是按秒計數,那咱就取time()結果的後五位,同一天之內後五位不會重復出現,比如今天0:0:0後五位是98765,那麼到今天23:59:59後五位就應該是98765+86400去掉最高位,相信這個應該是很好理解的。
這就產生了第二步的結果:
$order_sn = date('ymd').substr(time(),-5).substr(microtime(),2,5);
這樣一來也導致無法直觀的表達出訂單生成的時分秒,但我認為(或者說從業務角度理解)這個屬於可接受范圍。同時這樣處理出現重復訂單的概率就降低到了第一步的1/10,我以為這應該不算一個小數字。還不滿意?OK,那繼續!
想要繼續降低重復可能性那就繼續提高時間精度,但是我們的長度限制只有16位,看來只有減少部分不長變動的字元。
date(『ymd』)產生6位字元,而前兩位在一年之內都不會變化,第三到第四位也就是01-12。
前兩位我們可以使用A-Z的字母來表示,系統開始運行的那一年用A,第二年用B,第三年用C……類推,我相信我寫的這程序運行不了10年。第三第四位完全可以使用一位16進制數表示,這樣咱就又節約了兩位字元,這就可以在末尾加上00-99的隨機數。
現在看第三步的結果
$year_code = array('A','B','C','D','E','F','G','H','I','J');
$order_sn = $year_code[intval(date('Y'))-2010].
strtoupper(dechex(date('m'))).date('d').
substr(time(),-5).substr(microtime(),2,5).sprintf('d',rand(0,99));
理論上說出現重復訂單號的概率又降到了第二步的1/100。
做個簡單測試,寫個php文件,連續10次echo出這三步結果得到的$order_sn,中間無任何多餘程序。
第一種方案基本得到10個相同的結果。
第二種方案基本得到10個不同的結果,主要是後兩位不同,一般末位差一。
第三種方案得到10個不同結果末四位不同
當然這個測試不具備多少說服力
優點:
1、不用操作資料庫,性能較高。
2、較為直觀,不難看出訂單產生的大致時間
3、訂單號重復的概率極小,只有程序在百萬分之一秒內同時處理一個以上的生成訂單號請求,而且同時生成的0-99的隨機數也一樣才會出現重復的訂單號。
④ 資料庫有個訂單號是自動遞增的,要求格式為YYYYMMDD(代表日期)以及有0000-9999組成,請大蝦幫忙
你想錯了,在資料庫的那個訂單號是由程序產生的 ,插入資料庫,
string orderId= dateTime.now().tostring("YYYYMMDD")