Как создать уникальный номер заказа? - PullRequest
8 голосов
/ 15 февраля 2010

Я ищу хороший способ создания уникального идентификатора заказа. Вы видите проблемы с кодом ниже?

int customerId = 10000000;

long ticks = DateTime.UtcNow.Ticks;

long orderId = customerId + ticks;

int orderNumber = orderId.GetHashCode();

Я собираюсь проверить уникальность номера в базе данных перед созданием заказа.

Ответы [ 6 ]

25 голосов
/ 15 февраля 2010

Если вы храните свои записи в базе данных, вам действительно следует изучить имеющиеся там возможности для генерации уникальных суррогатных ключей. В SQLServer это будет поле IDENTITY и в Oracle это будет поле, которое использует SEQUENCE для генерации нового значения.

Если есть веская причина, по которой вы не можете использовать свою базу данных для генерации уникального ключа, вам следует взглянуть на что-то вроде Guid - у которого значение mucher выше вероятность манипуляции с датой и временем для создания уникального значения. Направляющие могут быть легко преобразованы в строки, поэтому в этом случае ваш идентификатор будет строкой.

То, что вы делаете с хэшами, не является хорошей идеей - ничто не гарантирует, что хэши будут уникальными - и во многих случаях они действительно сталкиваются. Руководства - не дают 100% гарантии уникальности на машинах , но на одной машине они всегда должны быть уникальными. И даже на разных машинах их вероятность столкновения крайне мала. Кроме того, использование машинного времени в качестве способа создания базовой ценности зависит от условий гонки (как те, что описывает Эрик).

Направляющие являются 128-битными значениями, поэтому вы не можете представлять их как простые int или long. Вам потребуется использовать строку в качестве идентификатора , что может или не может быть возможным в вашем случае, в зависимости от других соображений (например, независимо от того, управляете ли вы моделью данных или нет). Если вы можете использовать их, использовать 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 ); }
}


РЕДАКТИРОВАТЬ: В наше время убедительные причины для генерации уникальных идентификаторов на вашем прикладном уровне, а не в базе данных очень редки. Вы действительно должны использовать возможности, которые предоставляет база данных.

15 голосов
/ 15 февраля 2010

Предположим, у вас есть два идентификатора клиента, которые отличаются на 100, и оба они делают заказ с интервалом в 100 единиц времени. Ваша уникальность только что вышла в окно.

Вы говорите, что собираетесь проверить уникальность базы данных; Вы не говорите, что собираетесь делать, если произошло столкновение. Вы также не говорите, что собираетесь делать с условиями гонки; Предположим, что два идентификатора порядка столкновения созданы одновременно, и ни один не находится в базе данных. Вы спрашиваете базу данных в двух разных потоках, является ли элемент уникальным; это. Затем вы вводите оба, и уникальность была нарушена, даже если проверка была выполнена.

Это действительно очень плохой способ получить уникальность. Что было бы лучше, это переместить это в слой базы данных. Вы можете поддерживать глобальный потокобезопасный счетчик заказов и назначать каждому новому заказу следующий наивысший номер заказа.

Между прочим, в течение многих лет я задавал вариант этого вопроса в качестве вопроса технического интервью. Я заметил сильную корреляцию между набором людей, которые пытаются использовать время как источник уникальности, и набором людей, которых не нанимают. Время - ужасный источник уникальности; одновременно может произойти много разных вещей.

Что еще хуже, это использование случайных чисел. Случайные числа являются еще худшим источником уникальности, чем метки времени. Предположим, у вас есть действительно случайный генератор чисел, который генерирует случайные 32-битные целые числа для идентификаторов заказа. Сколько заказов вам нужно иметь до того, как шансы станут лучше чем пятьдесят на пятьдесят, чтобы вы сгенерировали два заказа с одинаковым идентификатором? Ответ удивляет многих: всего около 77 тысяч, прежде чем есть 50% -ная вероятность, что вы сгенерировали два заказа с одинаковым номером (и только 9300, пока нет вероятности 1%).

Помните: что вы ищете - это гарантия уникальности. Не вероятная уникальность, а железная гарантия того, что один номер заказа относится именно к одному заказу. Если это то, что вам нужно, убедитесь, что вы реализуете это.

7 голосов
/ 15 февраля 2010

Как насчет того, чтобы поле IDENTITY в базе данных делало это для вас?

Преимущество также будет в том, что удаленные / отмененные номера заказов не будут использоваться повторно (что хорошо или даже может потребоваться для учета).

0 голосов
/ 15 февраля 2010
0 голосов
/ 15 февраля 2010

Я бы использовал столбец IDENTITY, а если нет, то использовал System.Guid.NewGuid (), чтобы сгенерировать GUID для вас.

0 голосов
/ 15 февраля 2010

Если вы используете SQL Server, вам действительно стоит посмотреть спецификацию IDENTITY. Это позволяет делать это легко и быстро.

Ваше решение не уникально, потому что в системе все может происходить так быстро, что два процесса, выполняющихся последовательно или одновременно, могут получить одинаковое значение тика.

...