Использование GUID в качестве основного ключа

Обычно я использую идентификаторы автоматического инкремента в качестве основных ключей в базах данных. Я пытаюсь узнать преимущества использования GUID. Я прочитал эту статью: https://betterexplained.com/articles/the -Быстрый-руководство к Идентификаторы GUID /

Я понимаю, что эти GUID используются для идентификации объектов на уровне приложения. Они также хранятся в качестве первичного ключа на уровне базы данных. Например, скажем, у меня был следующий класс:

public class Person
{
public GUID ID;
public string Name;
..

//Person Methods follow
}

Скажем, я хотел создать нового человека в памяти, а затем вставить Person в базу данных. Могу ли я просто сделать это:

Person p1 = new Person();
p1.ID=GUID.NewGUID();
PersonRepository.Insert(p1);

Скажем, у меня была база данных, содержащая миллионы и миллионы строк с GUID в качестве основного ключа. Будет ли это всегда быть уникальным? Я даже правильно понимаю GUID?

Я прочитал эту статью ранее: http: //enterprisecraftsmanship.com/2014/11/15/cqs-with-database-generated-ids/. Это немного смущает меня, поскольку кажется, что рекомендуется рекомендовать счастливую среду между GUID и целыми числами в качестве основных ключей.

Редактировать 11/06/18

Я пришел к выводу, что гиды более подходят, чем ints для моих требований. Я использую CQRS больше в наши дни, а GUID лучше вписываются.

Я замечаю, что некоторые разработчики моделируют GUID как строки в модели домена, например. здесь: https://github.com/dotnet-architecture/eShopOnContainers/blob/dev/src/Services/Ordering/Ordering.Domain/AggregatesModel/BuyerAggregate/Buyer.cs - в этом случае: IdentityGuid - это GUID, смоделированный как строка. Есть ли причина делать это иначе, чем указано здесь: Использовать пользовательский объект значения или Guid как идентификатор объекта в распределенной системе? . Является ли «нормальным» моделирование GUID как строки или мне нужно моделировать его как GUID в модели и базе данных?

31 голос | спросил w0051977 2 PM00000070000003531 2017, 19:15:35

10 ответов


39

GUID по определению являются «глобально уникальными идентификаторами». В Java есть аналогичная, но немного другая концепция, называемая UUID «Универсально уникальные идентификаторы». Названия взаимозаменяемы для всего практического использования.

GUID являются ключевыми для того, как Microsoft предполагает создание кластеров баз данных, и если вам нужно включать данные из иногда связанных источников, они действительно помогают предотвратить столкновение данных.

Некоторые факты про-GUID:

  • GUID предотвращают конфликты клавиш
  • GUID помогают слиянием данных между сетями, машинами и т. д.
  • SQL Server поддерживает полупоследовательные GUID, чтобы минимизировать фрагментацию индекса ( ref , некоторые оговорки)

Некоторая Ugliness с GUID

  • Они большие, по 16 байт
  • Они вышли из строя, поэтому вы не можете сортировать идентификатор и надеяться получить порядок вставки, как вы можете, с помощью auto-increment ids
  • Они труднее работать, особенно на небольших наборах данных (например, искать таблицы)
  • Новая реализация GUID более надежна на SQL Server, чем в библиотеке C # (у вас могут быть последовательные GUID с SQL Server, в C # это случайные)

GUID сделают ваши индексы больше, поэтому стоимость дискового пространства для индексации столбца будет выше. Случайные GUID будут фрагментировать ваши индексы.

Если вы знаете, что не собираетесь синхронизировать данные из разных сетей, GUID могут нести дополнительные накладные расходы, чем они стоят.

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

ответил sanepete 6 Maypm14 2014, 14:57:48
25
  

Будет ли это всегда уникальным?

Всегда? нет, не всегда; это конечная последовательность бит.

  

Скажем, у меня была база данных, содержащая миллионы и миллионы строк с GUID в качестве основного ключа.

Миллионы и миллионы, вы, вероятно, безопасны. Значительно увеличивается миллион миллионов, а вероятность столкновения . Однако есть хорошие новости: к тому моменту вы уже исчерпали дисковое пространство.

  

Могу ли я это сделать?

Вы можете; это не совсем хорошая идея. Обычно ваша модель домена не должна генерировать случайные числа; они должны быть исходными для вашей модели.

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

См. описание основанного на имени uuid в RFC 4122

  

Является ли «нормальным» моделирование GUID в виде строки или мне нужно моделировать его как GUID в модели и базе данных?

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

Если GUID доступен как «примитивный тип» в агностической настройке вашего домена, я бы использовал его; он позволяет контексту поддержки выбирать подходящие оптимизации, которые могут быть доступны.

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

ответил VoiceOfUnreason 2 PM00000080000003331 2017, 20:07:33
10

GUID или UUID , скорее всего, будет уникальный из-за того, как они сгенерированы, и они обеспечивают безопасный способ гарантировать уникальность, не связываясь с центральным органом.

Преимущества GUID в качестве основного ключа:

  • Вы можете копировать данные между разными осколками кластера и не беспокоиться о столкновениях ПК.
  • Он позволяет вам узнать свой первичный ключ до того, как вы вставили какие-либо записи.
  • Упрощает логику транзакций для вставки дочерних записей.
  • Нельзя легко догадаться.

В приведенном примере:

Person p1 = new Person();
p1.ID = GUID.NewGUID();
PersonRepository.Insert(p1);

Указание идентификатора GUID перед вставкой может сохранить обратную поездку в базу данных при вставке последовательных дочерних записей и разрешить их совершать в одной транзакции.

Person p2 = new Person();
p2.ParentID = p1.ID
PersonRepository.Insert(p2);

Обманывает GUID как основной ключ:

  • Они большие 16 байтов, что означает, что они будут потреблять больше места, поскольку индексы и внешние ключи будут добавлены.
  • Они не сортируются хорошо, поскольку они являются по существу случайными числами.
  • Использование индекса очень, очень, очень плохо.
  • Много перемещений листа.
  • Их трудно запомнить.
  • Их трудно выразить словами.
  • Они могут сделать URL более трудным для чтения.

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

Многие базы данных имеют свои собственные внутренние реализации, которые пытаются смягчить проблемы хранения, вызванные GUID и SQL Server, даже имеют функцию newsequentialid , чтобы помочь с упорядочением UUID, позволяющим лучше использовать индексы, и они обычно имеют лучшие характеристики производительности.

Кроме того, с точки зрения тестировщика, пользователя или разработчика, работающего с приложением, использование идентификатора по GUID значительно улучшит связь. Представьте, что вам нужно читать GUID по телефону.

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

ответил icirellik 2 PM00000070000005031 2017, 19:34:50
4

Я бы сказал, нет, не используйте GUID как первичные ключи. Я на самом деле имею дело с такой БД сейчас, и они являются одной из основных причин проблем с производительностью.

Дополнительные 12 байт складываются быстро; помните, что большинство ПК будут FK в других таблицах, а всего три FK в таблице, у вас теперь есть 48 байт для каждой строки. Это добавляется в таблицу и индексы. Он также добавляется в дисковый ввод-вывод. Эти дополнительные 12 байтов должны быть прочитаны и записаны.

И если вы не используете последовательные контуры, а ПК кластеры (что и происходит по умолчанию), SQL время от времени придется перемещать целые страницы данных, чтобы сжать больше в нужное «пятно». Для очень транзакционной базы данных с множеством вставок, обновлений и удалений все быстро уходит.

Если вам нужен какой-то уникальный идентификатор синхронизации или что-то еще, добавьте столбец guid. Просто не делайте это ПК.

ответил Andy 3 AM00000030000002631 2017, 03:14:26
2
Person p1 = new Person();
p1.ID=GUID.NewGUID();
PersonRepository.Insert(p1);

Это, безусловно, самая важная причина для использования GUID.

Тот факт, что вы можете создать уникальный идентификатор без вашего кода, который знает или связывается с вашим уровнем персистентности, является огромным преимуществом.

Вы можете быть уверены, что объект Person, который вы только что создали на своем сервере, ПК, ноутбук, автономное устройство или что-то уникальное на всех ваших серверах по всему миру, однако распространяется.

Вы можете вставлять его в любой файл базы данных rdb или no-sql, файл, отправлять его в любой веб-сервис или немедленно отбрасывать его как не входящий

Нет, вы никогда не столкнетесь.

Да вставки могут быть немного медленнее, так как индексу, возможно, придется поиграть с.

Да, он больше, чем int.

  • редактировать. пришлось отстреливаться, прежде чем закончиться.

Я знаю, что многие люди сильно чувствуют себя в auto inc ints, и это спорная тема с администраторами баз данных

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

auto inc ints имеет много недостатков

  • Вы используете распределенный db без использования Sql. Вы просто не можете поговорить со всеми другими экземплярами, чтобы узнать, что такое следующий номер.

  • Используется система очереди сообщений. Вещи нуждаются в идентификаторах, прежде чем они попадут в db

  • Вы создаете несколько элементов и редактируете их перед сохранением. Каждому нужен идентификатор, прежде чем вы нажмете db

  • Вы хотите удалить и повторно вставить строки. Удостоверьтесь, что вы не подсчитываете свои идентификаторы auto inc и заканчиваетесь!

  • Вы не хотите раскрывать, сколько заказов вы приняли в этом году каждому пользователю

  • Вы хотите перенести анонимные данные из производства, чтобы протестировать и сохранить взаимосвязи. Но не удалять все существующие тестовые данные.

  • Вы хотите объединить один продукт-арендатор в многодоменную базу данных, но каждый имеет заказ 56.

  • Вы создаете объекты, которые сохраняются, но эфемерны. (неполные заказы) снова, не используйте все свои ints с вещами, которые больше не существуют.

Список бесконечен, и все они являются реальными проблемами, которые происходят с людьми все время. в отличие от исчерпания дискового пространства из-за немного больших FK cols

Наконец, массивная проблема с ints у вас заканчивается !!! нормально в теории вы не, есть нагрузки. Но на практике вы это делаете, потому что люди не воспринимают их как случайные числа без смысла. они делают что-то вроде

  • О, я не хочу, чтобы клиенты думали, что мы новы. начать с 10000

  • Мне пришлось импортировать нагрузку данных, поэтому я только увеличил семя до 1 м, чтобы мы знали, что импортировано

  • нам нужны данные категории. каждый период начинается со следующего миллиона, поэтому мы можем использовать первые цифры как магическое число

  • Я удалил и повторно импортировал все данные с новыми идентификаторами. Да даже журналы аудита.

  • используйте этот номер, который является составным ключом, как идентификатор этой другой вещи

ответил Ewan 2 PM00000090000005831 2017, 21:14:58
2
  

Я понимаю, что эти GUID используются для идентификации объектов на уровне приложения. Они также хранятся в качестве первичного ключа на уровне базы данных.

Вот где вы должны остановиться, прямо там и передумать.

Первичный ключ вашей базы данных НИКОГДА не должен иметь делового значения. По определению это должно быть бессмысленным.

Поэтому добавьте GUID в качестве бизнес-ключа и обычный первичный ключ (обычно длинный int) в качестве первичного ключа базы данных. Вы всегда можете указать уникальный идентификатор GUID для обеспечения уникальности.

Это, конечно, теория базы данных, но это хорошая практика. Я имел дело с базами данных, где первичные ключи имели бизнес-смысл (один клиент, как полагали, сохранил некоторые ресурсы базы данных, используя их как номера сотрудников, номера клиентов и т. Д. И т. Д., Например), и это всегда приводит к проблемам.

ответил jwenting 3 PM00000040000003831 2017, 16:45:38
2

Всегда использовать сгенерированные базы данных, автоматически увеличивая первичные ключи (ПК).

Зачем использовать auto-incrementing вместо GUID /UUID?

  • GUID (UUID) s не предотвращает ключевые конфликты, поскольку они не уникальны, и нет возможности сделать их уникальными, поскольку они генерируются из многочисленных источников.
  • GUID не помогают слиянием, так как они значительно увеличивают инертный процесс слияния с чрезвычайно длинными, нецелочисленными столбцами PK и FK, которые занимают много времени для обработки. Помните, что для большинства ПК будет, по крайней мере, одна другая таблица с не менее чем двумя ключами одинакового размера: это собственный ПК и FK для первой таблицы. Все должны быть разрешены в слиянии.

Но как тогда обрабатывать осколки, кластеры и т. д.?

  • Создание многоколоночных PK, состоящих из отдельных столбцов, идентифицирующих каждый осколок /кластер /базу данных /все, что управляет его собственными автоинкрементными ключами. Например ...

3-столбцовый PK для кластеризованной таблицы может быть ...

 DB | SH | KEY     |
----|----|---------|
 01 | 01 | 1234567 |

Но что о ...?

  • Несколько поездок в базу данных. Большинство приложений не нужно однозначно идентифицировать создаваемую запись, пока она не будет вставлена ​​в базу данных, поскольку этот поток /сеанс /все работает только по одному. Если приложение действительно нуждается в этой способности, используйте созданное приложением временное PK , которое не отправляется в базу данных . Пусть база данных затем поместит ее собственный автоинкремент PK в строку, когда она вставлена. Вставки будут использовать временную PK, в то время как обновления и удаления будут использовать постоянный PK, назначенный базой данных.

  • Производительность. Компьютеры могут обрабатывать простые целые числа намного быстрее, чем что-либо еще, из-за значительно более крупного домена, если возможно, значения для каждого элемента в GUID (37) по сравнению с целым числом (10). Помните также, что каждый символ в GUID должен быть сначала преобразован в число, которое будет обрабатываться CPU.

Общие недомогания первичных ключей У ПК есть только одна цель ... абсолютно однозначно идентифицировать строку в таблице. Все остальное является слишком распространенным злоупотреблением.

Обнаружение пропущенных записей

  • Отсутствующие записи не могут быть обнаружены при просмотре ПК. Благословение QA, по крайней мере, для обеспечения качества данных. Однако они и недостаточное понимание программистами того, как назначаются ключи в современных системах баз данных, часто приводят их к недоверию о том, что недостающее число в автоматическом инкрементальном ПК означает отсутствие данных. Он не , потому что ...
  • Для производительности системы баз данных выделяют блоки чисел в «последовательностях» (партии, диапазоны), чтобы минимизировать поездки в фактическую базу данных в хранилище. Размер этих последовательностей чисел часто находится под контролем DBA, но не может настраиваться на основе таблицы.
  • Ключевым выводом является ... неиспользуемые числа из этих последовательностей никогда не возвращаются в базу данных, поэтому всегда есть пробелы в номерах ПК.
  • Почему бы вам не использовать неиспользуемые номера? Поскольку различные действия по обслуживанию базы данных могут привести к остановке последовательности. Это такие вещи, как перезагрузки, массовая перезагрузка таблиц, некоторые виды восстановления из резервных копий и некоторые другие операции.

Сортировка

  • Сортировка по PK очень подвержена ошибкам, так как большинство людей думает, что оно перечисляет строки в том порядке, в котором они были созданы, и что соответствует часовому времени. В основном, но не обязательно.
  • Механизмы базы данных оптимизированы для максимальной производительности, и это может означать задержку вставки результатов долговременной сложной транзакции, чтобы вставить короткие простые, так сказать, «вне очереди».
ответил DocSalvager 6 AM00000070000002831 2017, 07:53:28
1

Как и все, есть преимущества и недостатки:

Хорошее:

  1. Ваши ключи всегда одинаковой длины (очень большие базы данных могут иметь очень большие клавиши)

  2. Уникальность в значительной степени гарантирована - даже когда вы создаете их из отдельной системы и /или не читали последний идентификатор из базы данных

Плохо:

  1. Как упоминалось выше - более крупные индексы и хранилище данных.

  2. Вы не можете заказать по ID, вам нужно заказать что-то еще. Больше индексов, возможно, менее эффективно.

  3. Они менее понятны для людей. Целые, как правило, проще разбирать, запоминать и печатать для людей. Использование идентификаторов GUID в качестве идентификаторов в предложениях WHERE через несколько соединенных таблиц может привести к таянию головы.

Как и все, используйте их там, где это уместно, не будьте догматичными - во многих ситуациях автоматическое добавление целых чисел лучше, иногда GUID отлично.

ответил Phil S 3 PM00000060000004331 2017, 18:24:43
0

Да, вы можете использовать GUID как первичный ключ. Нижняя сторона - это размер и быстрая фрагментация индекса.

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

ответил paparazzo 3 PM00000040000004931 2017, 16:52:49
0

Вот мой вопрос по этой проблеме - решение представляет собой промежуточное место между значениями GUID и int, максимально использующими оба.

Класс генерирует псевдослучайное (но увеличивающееся во времени) значение Id, которое аналогично Comb GUID .

Ключевым преимуществом является то, что он позволяет генерировать значения идентификатора на клиенте, а не использовать значения автоматического прироста, генерируемые на сервере (что требует округления) с почти нулевым риском дублирования значений.

Сгенерированные значения используют только 8 байтов, а не 16 для GUID, и не зависят от одного конкретного порядка сортировки базы данных (например, Sql Server для GUID ). Значения могут быть расширены, чтобы использовать весь беззнаковый длинный диапазон, но это может вызвать проблемы с любой базой данных или другим репозиторием данных, которые только подписали целочисленные типы.

public static class LongIdGenerator
{
    // set the start date to an appropriate value for your implementation 
    // DO NOT change this once any application that uses this functionality is live, otherwise existing Id values will lose their implied date
    private static readonly DateTime PeriodStartDate = new DateTime(2017, 1, 1, 0, 0, 0, DateTimeKind.Utc);
    private static readonly DateTime PeriodEndDate = PeriodStartDate.AddYears(100);
    private static readonly long PeriodStartTicks = PeriodStartDate.Ticks;
    private static readonly long PeriodEndTicks = PeriodEndDate.Ticks;
    private static readonly long TotalPeriodTicks = PeriodEndTicks - PeriodStartTicks;

    // ensures that generated Ids are always positve
    private const long SEQUENCE_PART_PERMUTATIONS = 0x7FFFFFFFFFFF; 

    private static readonly Random Random = new Random();

    private static readonly object Lock = new object();
    private static long _lastSequencePart;

    public static long GetNewId()
    {
        var sequencePart = GetSequenceValueForDateTime(DateTime.UtcNow);

        // extra check, just in case we manage to call GetNewId() twice before enough ticks have passed to increment the sequence 
        lock (Lock)
        {
            if (sequencePart <= _lastSequencePart)
                sequencePart = _lastSequencePart + 1;

            _lastSequencePart = sequencePart;
        }

        // shift so that the sequence part fills the most significant 6 bytes of the result value
        sequencePart = (sequencePart << 16);

        // randomize the lowest 2 bytes of the result, just in case two different client PCs call GetNewId() at exactly the same time
        var randomPart = Random.Next() & 0xFFFF;

        return sequencePart + randomPart;
    }

    // used if you want to generate an Id value for a historic time point (within the start and end dates)
    // there are no checks, compared to calls to GetNewId(), but the chances of colliding values are still almost zero
    public static long GetIdForDateTime(DateTime dt)
    {
        if (dt < PeriodStartDate || dt > PeriodStartDate)
            throw new ArgumentException($"value must be in the range {PeriodStartDate:dd MMM yyyy} - {PeriodEndDate:dd MMM yyyy}");

        var sequencePart = GetSequenceValueForDateTime(dt.ToUniversalTime());
        var randomPart = Random.Next() & 0xFFFF;
        return ( sequencePart << 16 ) + randomPart;
    }

    // Get a 6 byte sequence value from the specified date time - startDate => 0 --> endDate => 0x7FFFFFFFFFFF
    // For a 100 year time period, 1 unit of the sequence corresponds to about 0.022 ms
    private static long GetSequenceValueForDateTime(DateTime dt)
    {
        var ticksFromStart = dt.ToUniversalTime().Ticks - PeriodStartTicks;
        var proportionOfPeriod = (decimal)ticksFromStart / TotalPeriodTicks;
        var result = proportionOfPeriod * SEQUENCE_PART_PERMUTATIONS;
        return (long)result;
    }

    public static DateTime GetDateTimeForId(long value)
    {
        // strip off the random part - the two lowest bytes
        var timePart = value >> 16;
        var proportionOfTotalPeriod = (decimal) timePart / SEQUENCE_PART_PERMUTATIONS;
        var ticks = (long)(proportionOfTotalPeriod * TotalPeriodTicks);
        var result = PeriodStartDate.AddTicks(ticks);
        return result;
    }
}
ответил Peregrine 12 J0000006Europe/Moscow 2018, 16:15:32

Похожие вопросы

Популярные теги

security × 330linux × 316macos × 2827 × 268performance × 244command-line × 241sql-server × 235joomla-3.x × 222java × 189c++ × 186windows × 180cisco × 168bash × 158c# × 142gmail × 139arduino-uno × 139javascript × 134ssh × 133seo × 132mysql × 132