Таблицы без первичного ключа

У меня есть несколько таблиц, единственными уникальными данными которых является столбец uniqueidentifier (Guid). Поскольку направляющие не являются последовательными (и они генерируются на стороне клиента, поэтому я не могу использовать newsequentialid()), я создал не первичный некластеризованный индекс для этого поля идентификатора вместо того, чтобы давать таблицам кластеризованный первичный индекс. ключ.

Мне интересно, каковы последствия производительности для этого подхода. Я видел, как некоторые люди предполагают, что таблицы должны иметь автоинкрементный ("идентификатор") int в качестве кластеризованного первичного ключа, даже если он не имеет никакого смысла, так как это означает, что ядро ​​базы данных может использовать это значение для быстрого ищите строку вместо того, чтобы использовать закладку.

Моя база данных реплицирована слиянием на нескольких серверах, поэтому я уклонился от столбцов identity int, так как они немного сложны для правильной репликации.

о чем ты думаешь? Должны ли таблицы иметь первичные ключи? Или это нормально, если у вас нет кластерных индексов, если нет разумных столбцов для индексации таким образом?

7 ответов

Решение

При работе с индексами вы должны определить, для чего будет использоваться ваша таблица. Если вы в первую очередь вставляете 1000 строк в секунду и не выполняете никаких запросов, то кластеризованный индекс снижает производительность. Если вы выполняете 1000 запросов в секунду, отсутствие индекса приведет к очень низкой производительности. Лучше всего при настройке запросов / индексов лучше всего использовать анализатор плана запросов и SQL Profiler в SQL Server. Это покажет вам, где вы работаете с дорогостоящим сканированием таблиц или другими блокировщиками производительности.

Что касается аргумента GUID vs ID, вы можете найти людей онлайн, которые клянутся обоими. Меня всегда учили использовать GUID, если у меня нет действительно веской причины не делать этого. У Джеффа есть хороший пост, в котором рассказывается о причинах использования идентификаторов GUID: https://blog.codinghorror.com/primary-keys-ids-versus-guids/.

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

[Редактировать] @Matt, после более подробного изучения дебатов по GUID/ID, я наткнулся на этот пост. Как я уже говорил, нет правильного или неправильного ответа. Это зависит от ваших конкретных потребностей реализации. Но вот несколько довольно веских причин использовать GUID в качестве первичного ключа:

Например, существует проблема, известная как "горячая точка", когда определенные страницы данных в таблице находятся в состоянии относительно высокой конкуренции за валюту. По сути, происходит то, что большая часть трафика в таблице (и, следовательно, блокировки на уровне страниц) происходит в небольшой области таблицы, ближе к концу. Новые записи всегда будут поступать в эту точку доступа, потому что IDENTITY - это генератор последовательных чисел. Эти вставки являются проблематичными, потому что они требуют исключительной блокировки страницы на странице, к которой они добавлены (точка доступа). Это эффективно сериализует все вставки в таблицу благодаря механизму блокировки страниц. NewID(), с другой стороны, не страдает от горячих точек. Значения, созданные с помощью функции NewID (), являются последовательными только для коротких пакетов вставок (когда функция вызывается очень быстро, например, во время многострочной вставки), что приводит к тому, что вставленные строки распределяются случайным образом по страницам данных таблицы. всего в конце - таким образом устраняя горячую точку от вставок.

Кроме того, поскольку вставки распределяются случайным образом, вероятность разбиения страницы значительно снижается. Хотя страница разделена здесь и там не так уж и плохо, эффекты быстро складываются. С IDENTITY, коэффициент заполнения страницы довольно бесполезен в качестве механизма настройки и может также быть установлен на 100% - строки никогда не будут вставлены ни на одну страницу, кроме последней. С помощью NewID () вы можете фактически использовать Fill Factor как инструмент повышения производительности. Вы можете установить коэффициент заполнения на уровень, который приблизительно соответствует ожидаемому росту объема между перестройками индекса, а затем запланировать перестройки в непиковые часы с помощью переиндексации dbcc. Это эффективно задерживает скачки производительности при разделении страниц до непикового времени.

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

Еще одним огромным преимуществом использования GUID для PK является тот факт, что значение действительно гарантированно уникально - не только среди всех значений, сгенерированных этим сервером, но и всех значений, сгенерированных всеми компьютерами - будь то ваш db-сервер, веб-сервер, сервер приложений или клиентский компьютер. Практически каждый современный язык имеет возможность генерировать действительный guid - в.NET вы можете использовать System.Guid.NewGuid. Это ОЧЕНЬ удобно, когда речь идет, в частности, о кешированных наборах данных master-detail. Вам не нужно использовать сумасшедшие схемы временных ключей, чтобы связать ваши записи вместе, прежде чем они будут зафиксированы. Вы просто выбираете совершенно правильный новый Guid из операционной системы для значения постоянного ключа каждой новой записи во время ее создания.

http://forums.asp.net/t/264350.aspx

Просто прыгаю, потому что Мэтт немного меня травил.

Необходимо понимать, что хотя кластерный индекс по умолчанию помещается в первичный ключ таблицы, эти две концепции являются отдельными и должны рассматриваться отдельно. CIX указывает способ хранения данных и обращения к ним в NCIX, тогда как PK обеспечивает уникальность для каждой строки, чтобы удовлетворить ЛОГИЧЕСКИЕ требования таблицы.

Таблица без CIX - это просто куча. Стол без ПК часто считается "не столом". Лучше всего разбираться в понятиях PK и CIX по отдельности, чтобы вы могли принимать разумные решения при проектировании баз данных.

обкрадывать

Первичный ключ служит трем целям:

  • указывает на то, что столбцы должны быть уникальными
  • указывает, что столбец (столбцы) должен быть ненулевым
  • задокументировать намерение, что это уникальный идентификатор строки

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

Третья причина хороша:

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

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

Никто не ответил на актуальный вопрос: каковы плюсы / минусы таблицы с NO PK, NOR и CLUSTERED. На мой взгляд, если вы оптимизируете для более быстрой вставки (особенно для инкрементальной массовой вставки, например, когда вы загружаете данные в непустую таблицу), такая таблица: без кластеризованного индекса, без ограничений, без внешних ключей, без значений по умолчанию и НЕТ первичного ключа, в базе данных с простой моделью восстановления, является лучшим. Теперь, если вы когда-нибудь захотите запросить эту таблицу (а не сканировать ее полностью), вы можете добавить некластеризованные неуникальные индексы по мере необходимости, но сведите их к минимуму.

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

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

Вместо этого первичный ключ должен представлять собой минимальный набор атрибутов (обратите внимание, что большинство СУБД допускают составной первичный ключ), который однозначно идентифицирует кортеж.

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

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

Так как вы делаете репликацию, ваши правильные личности - это то, что нужно избегать. Я бы сделал ваш GUID первичным ключом, но не кластеризованным, так как вы не можете использовать newsequentialid. Это поражает меня как ваш лучший курс. Если вы не сделаете это PK, а поместите в него уникальный индекс, рано или поздно это может привести к тому, что люди, которые поддерживают систему, не будут правильно понимать отношения FK, приводящие к ошибкам.

Другие вопросы по тегам