简体   繁体   English

如何生成唯一的订单号?

[英]How to generate unique order number?

I'm looking for a good way to generate a unique order ID. 我正在寻找一种生成唯一订单ID的好方法。 Can you see any problems with the code below? 你能看到下面代码有什么问题吗?

int customerId = 10000000;

long ticks = DateTime.UtcNow.Ticks;

long orderId = customerId + ticks;

int orderNumber = orderId.GetHashCode();

I'm going to check that the number is unique in the database before creating the order. 在创建订单之前,我将检查数据库中的数字是否唯一。

If you are storing your records in a database, you should really look into the capabilities available there to generate unique surrogate keys. 如果要将记录存储在数据库中,则应该真正研究可用于生成唯一代理键的功能。 In SQLServer this would be an IDENTITY field and in Oracle it would be a field that uses a SEQUENCE to generate a new value. 在SQLServer中,这将是一个IDENTITY字段,在Oracle中,它将是一个使用SEQUENCE生成新值的字段。

If there's a compelling reason why you can't use your database to generate a unique key, you should look at something like a Guid - which has a mucher higher probability than date-time manipulation to generate a unique value. 如果有一个令人信服的理由为什么你不能使用你的数据库来生成一个唯一的密钥,你应该看一下类似Guid东西 - 它比日期时间操作具有更高概率的mucher来生成一个唯一值。 Guids can be trivially converted into strings, so your identifier would be a string in this case. Guid可以简单地转换为字符串,因此在这种情况下您的标识符将是一个字符串。

What you are doing with hashes is not a good idea - nothing gaurantees that hashes will be unique - and in many cases they do indeed collide. 你用哈希做什么并不是一个好主意 - 没有任何关于哈希值得特别注意的事情 - 在很多情况下它们确实会发生碰撞。 Guids - don't provide a 100% guarantee of uniqueness across machines but on a single machine they should always be unique. Guids - 不提供100%保证机器的唯一性但在一台机器上它们应始终是唯一的。 And even across machines, their chances of collision are extremely remote. 甚至在机器上,他们碰撞的机会极其遥远。 Also, using machine time as a way to build up the underlying value is subject to race conditions (like those Eric describes). 此外,使用机器时间作为构建潜在价值的方式受竞争条件的影响(如Eric所述)。

Guids are 128-bit values, so you can't represent them as a simple int or long . Guids是128位值,因此您不能将它们表示为简单的intlong It would require you to use string as your IDs , which may or may not be possible in your case, depending on other considerations (like whether or not you control the data model). 它需要您使用字符串作为您的ID ,在您的情况下可能会或可能不会,这取决于其他考虑因素(例如您是否控制数据模型)。 If can use them, using a Guid is really easy: 如果可以使用它们,使用Guid非常简单:

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

If you really must use a numeric identifier, and you are willing to abandon easily scaling your application across multiple servers, you could use an auto-incrementing global number to supply a unique key. 如果您确实必须使用数字标识符,并且您愿意放弃在多个服务器上轻松扩展应用程序,则可以使用自动递增的全局数字来提供唯一键。 You would have to seed this number with the next available value (max+1) from your database when the application starts up. 在应用程序启动时,您必须使用数据库中的下一个可用值(max + 1)为此数字设定种子。 You would also have to then protect this value from concurrent use from multiple threads. 然后,您还必须保护此值不受多个线程的并发使用的影响。 I would wrap this responsibility in a class: 我会把这个责任包括在一个课程中:

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 ); }
}


EDIT: In this day and age, compelling reasons for generating unique IDs in your application layer rather than at the database are very uncommon. 编辑: 在这个时代,在应用程序层而不是在数据库中生成唯一ID的令人信服的理由非常罕见。 You should really use the capabilities that the database provides. 您应该真正使用数据库提供的功能。

Suppose you have two customer ids that differ by 100, and they happen to both make an order that is 100 time units apart. 假设您有两个相差100的客户ID,并且它们碰巧订单相隔100个单位。 Your uniqueness just went out the window. 你的独特性就在窗外。

You say that you're going to check the database for uniqueness; 你说你要检查数据库的唯一性; you don't say what you're going to do if there's a collision. 如果发生碰撞,你不会说你要做什么。 You also don't say what you're going to do about race conditions; 你也没有说你将如何处理竞争条件; suppose two colliding order ids are created at the same time, neither is in the database. 假设同时创建了两个冲突订单ID,数据库中也没有。 You ask the database on two different threads whether the item is unique; 您在两个不同的线程上询问数据库是否该项是唯一的; it is. 它是。 You then enter both, and uniqueness has been violated even though the check has been done. 然后输入两者,即使检查已完成,也违反了唯一性。

This is a really, really bad way to get uniqueness. 这是获得独特性的一种非常非常糟糕的方式。 What would be better is to move this into the database layer. 更好的是将其移动到数据库层。 You can maintain a global, threadsafe counter of orders and assign each new order the next highest order number. 您可以维护一个全局的线程安全的订单计数器,并为每个新订单分配下一个最高订单号。

Incidentally, for many years I have asked a variation on this question as a technical interview question. 顺便说一下,多年来我一直在问这个问题的变体作为技术面试问题。 I've noticed a strong correlation between the set of people who attempt to use time as a source of uniqueness and the set of people who don't get hired. 我注意到那些试图利用时间作为唯一性来源的人和那些没有被雇用的人之间存在很强的相关性。 Time is a terrible source of uniqueness; 时间是独特的可怕来源; lots of different things can happen at the same time. 很多不同的事情可以同时发生。

What is even worse is using random numbers. 更糟糕的是使用随机数。 Random numbers are an even worse source of uniqueness than timestamps. 随机数是比时间戳更糟糕的唯一性来源。 Suppose you have a truly random number generator that generates random 32 bit integers for order IDs. 假设您有一个真正的随机数生成器,可为订单ID生成随机32位整数。 How many orders do you need to have before the odds are better than fifty-fifty that you've generated two orders with the same ID? 在赔率高于五十五美元之前,您需要多少订单才能生成两个具有相同ID的订单? The answer surprises a lot of people: it's only about 77 thousand before there is a 50% chance that you've generated two orders with the same number (and only 9300 until there's a 1% chance.) 这个答案让很多人感到惊讶:在你有50%的可能性生成两个相同数量的订单之前,它只有大约77,000个(只有9300,直到有1%的机会。)

Remember: what you are after is a guarantee of uniqueness. 记住:你所追求的是对独特性的保证 Not a probable uniqueness, but an iron-clad guarantee that one order number refers to exactly one order. 不是一个可能的唯一性,而是一个铁板保证,一个订单号只指一个订单。 If that's what you need, then make sure you implement that. 如果这就是您所需要的,那么请确保实现它。

What about having an IDENTITY field in the database do it for you? 那么在数据库中有一个IDENTITY字段可以帮你吗?

It will also have the advantage that deleted/canceled order numbers won't be reused (which is good or may even be required for accounting). 它还具有以下优点:删除/取消的订单号不会被重复使用(这是好的,甚至可能是会计要求)。

IF you're using SQL Server, you should really look up the IDENTITY specification. 如果您使用的是SQL Server,那么您应该查找IDENTITY规范。 It lets you do this with ease and speed. 它可以让您轻松快捷地完成此任务。

Your solution isn't unique because things can happen so fast in the system that two processes, either running in sequence or concurrently, can get the same tick value. 您的解决方案并不是唯一的,因为系统中的事情可能发生得如此之快,以至于两个进程(按顺序或同时运行)可以获得相同的tick值。

我将使用IDENTITY列,如果不是,请使用System.Guid.NewGuid()为您生成GUID。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM