2010-02-15 167 views
8

我正在尋找一種生成唯一訂單ID的好方法。你能看到下面的代碼有什麼問題嗎?如何生成唯一的訂單號碼?

int customerId = 10000000; 

long ticks = DateTime.UtcNow.Ticks; 

long orderId = customerId + ticks; 

int orderNumber = orderId.GetHashCode(); 

我將在創建訂單前檢查數據庫中的數字是否唯一。

+2

從理論上講,哈希會發生碰撞。 – 2010-02-15 16:11:00

+0

當然,它不是唯一的你正在尋找,只是下一個序列號......我同意下面的人談論使用身份專欄。 – Paddy 2010-02-15 16:16:33

+3

@開發者藝術:對此毫無理論依據。哈希碰到*所有時間*。只有大約40億可用,所以它們當然會碰撞。 – 2010-02-15 16:47:50

回答

7

什麼有關數據庫中具有識別領域爲你做?

它也將有刪除/取消的訂單數不會重複使用(這是好還是甚至可能需要會計)的優勢。

0

如果你使用SQL Server,你應該看看了IDENTITY規範。它可以讓你輕鬆快速地完成這項任務。

您的解決方案不是唯一的,因爲事情可以在兩個過程,無論是在序列運行或同時系統這麼快發生,可以得到相同的刻度值。

+0

客戶ID會因每個訂單而有所不同。我認爲將客戶ID與滴答聲結合起來應該能產生我以後的獨特價值。 – 2010-02-15 16:12:05

24

如果您在數據庫中存儲你的記錄,你真的應該看看那裏提供生成唯一代理鍵的功能。在SQLServer中,這將是IDENTITY字段,而在Oracle中,它將是一個使用SEQUENCE生成新值的字段。

如果有一個令人信服的理由,爲什麼你不能使用你的數據庫,生成一個唯一的密鑰,你應該看看像一個Guid - 具有mucher更高的概率比日期時間操作產生一個獨特的價值。 Guids可以簡單地轉換成字符串,所以在這種情況下你的標識符將是一個字符串。

你正在做什麼與哈希不是一個好主意 - 沒有什麼gaurantees哈希將是唯一的 - 並在許多情況下,他們確實發生碰撞。 Guids - 不提供跨機器的唯一性的100%保證,但在單臺機器上,它們應始終是唯一的。即使在機器上,他們碰撞的機會也非常遙遠。此外,使用機器時間作爲構建基礎價值的一種方式受制於競爭條件(如Eric所描述的那樣)。

Guid是128位值,因此您不能將它們表示爲簡單的intlong。這將需要你使用字符串作爲你的ID,根據其他考慮(比如你是否控制數據模型),你的情況可能會也可能不會。如果可以使用它們,使用GUID是很容易的:

string customerId = Guid.NewGuid().ToString(); // fetch new guid and save as string 
string orderNumber = Guid.NewGuid().ToString(); // same story here... 

如果你真的必須使用數字標識符,你是願意放棄輕鬆擴展在多個服務器上的應用程序,你可以使用自動增量全球號碼提供一個唯一的密鑰。當應用程序啓動時,您將不得不使用數據庫中的下一個可用值(最大+ 1)爲此編號。您還必須保護此值免受多個線程的併發使用。我想換這個責任在一類:

class static UniqueIDGenerator 
{ 
    // reads Max+1 from DB on startup 
    private static long m_NextID = InitializeFromDatabase(); 

    public static long GetNextID() { return Interlocked.Increment(ref m_NextID); } 
} 


編輯:在這個時代,在應用程序層,而不是在數據庫生成唯一ID令人信服的理由是非常少見。你應該真的使用數據庫提供的功能。

+4

+1 - 非常好的答案。 – JasCav 2010-02-15 16:29:31

0

我會使用IDENTITY列,如果沒有,使用System.Guid.NewGuid()爲您生成一個GUID。

14

假設您有兩個客戶ID相差100,他們碰巧訂購的時間間隔爲100個時間單位。你的獨特性就在窗外。

你說你要檢查數據庫的唯一性;如果碰撞發生,你不會說你要做什麼。你也沒有說出你將要對比賽條件做什麼;假設同時創建兩個碰撞順序標識符,兩個都不在數據庫中。您在兩個不同的線程上詢問數據庫是否該項目是唯一的;它是。然後輸入兩者,即使檢查完成,唯一性也被違反。

這是一個非常非常糟糕的方法來獲得獨特性。更好的是將其移入數據庫層。您可以維護訂單的全局線程安全計數器,併爲每個新訂單分配下一個最高訂單號。

順便說一句,多年來,我已經問過這個問題的變化作爲技術面試問題。我注意到一羣試圖利用時間作爲唯一性來源的人和那些沒有被僱用的人之間的強烈關聯。時間是可怕的唯一來源; 許多不同的事情可以同時發生。

更糟糕的是使用隨機數字。隨機數字比時間戳更獨特。假設你有一個真正的隨機數發生器,它爲訂單ID生成隨機的32位整數。在賠率優於五十五之前,您需要有多少訂單才能生成兩個具有相同ID的訂單?答案讓很多人驚訝:它只有大約77萬,之前有50%的機會可以生成兩個訂單,並且數量相同(並且只有9300,直到有1%的機會。)

請記住:你以後是一個保證的唯一性。不是可能性的唯一性,但鐵包裝保證一個訂單號碼恰好指一個訂單。如果這就是你需要的,那麼確保你實現了。

0
+1

如果您建議使用真隨機隨機生成訂單ID,這是一個*危險的可怕*想法。在兩次真正的隨機32位數之間發生衝突的機率在77000次嘗試後上升到> 50%! – 2010-02-15 17:53:03

+0

@Eric:你說的沒錯,我不是建議隨機生成一個訂單ID,只需要生成一個數字就需要了解這類操作的陷阱,就像你指出的那樣。隨機數理論是找到這樣的陷阱的好地方,所以可以避免它們。 – 2010-02-15 18:21:54