2010-02-15 169 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