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

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

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

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

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

Ответов (7)

Решение

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Роб

Никто не ответил на актуальный вопрос: каковы плюсы / минусы таблицы с NO PK ИЛИ КЛАСТЕРИРОВАННЫМ индексом. На мой взгляд, если вы оптимизируете для более быстрых вставок (особенно инкрементную массовую вставку, например, когда вы массово загружаете данные в непустую таблицу), такая таблица: без кластерного индекса, без ограничений, без внешних ключей, без значений по умолчанию и НЕТ Первичный ключ в базе данных с простой моделью восстановления - лучший вариант. Теперь, если вы когда-нибудь захотите запросить эту таблицу (а не сканировать ее полностью), вы можете добавить некластеризованные неуникальные индексы по мере необходимости, но сведите их к минимуму.

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