Почему первичные ключи генерируются идентификаторами GUID aspnet_regsql?

Я разрабатываю веб-сайт ASP.NET в Visual Studio 2010 (с пакетом обновления 1, большое спасибо). Я хочу использовать встроенные поставщики членства и ролей .NET для SQL Server 2008.

Я занимаюсь разработкой технологий Microsoft в течение очень долгого времени и общался с некоторыми из лучших администраторов баз данных SQL Server в бизнесе. Каждый из них советовал мне держаться подальше от использования GUIDS в качестве первичных ключей при построении таблицы базы данных, которая будет:

  1. Иметь очень большое количество записей.
  2. Иметь большой объем вставок и удалений.

Причина: потому что первичный ключ является кластеризованным индексом!

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

Это было бы прекрасно для таблицы с несколькими тысячами записей или около того. SQL Server пришлось бы перепозиционировать только несколько. Однако, если таблица данных содержит несколько миллионов записей и обнаруживает, что она должна вставить новую запись в строку 216, это может занять значительное время (по веб-стандартам). Он должен физически переместить все эти строки вниз, чтобы вставить новую.

Так что мой вопрос просто это. Поскольку Microsoft и все DBS, которые мы знаем и любим, сказали НЕТ GUID в качестве первичных ключей... почему инструмент ASPNET_REGSQL создает таблицы, используя GUID в качестве первичного ключа?

Или я что-то упускаю? Есть ли в движке SQL Profiler в 2008 г. новая функция, которая больше не рассматривает GUIDS как задачу?


person dotnettex    schedule 26.04.2011    source источник
comment
Последовательные идентификаторы GUID — одно из решений этой проблемы. Тем не менее, я не фанат GUID для ключей.   -  person kenwarner    schedule 26.04.2011


Ответы (2)


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

Что касается того, что вы сказали о нескольких миллионах строк данных, с гидами все будет в порядке, если вы всегда просите сервер SQL вернуть одну строку данных. Самая большая проблема будет, когда вы запрашиваете какое-то большое подмножество данных или когда вы пакетно вставляете большое количество строк; Тогда вы, вероятно, будете выполнять много случайных операций ввода-вывода, чтобы получить все строки, соответствующие вашим критериям, или вставить все строки в случайные места, на которые в конечном итоге указывают guids. Кроме того, SQL не нужно «физически перемещать все эти строки вниз, чтобы вставить новую»; Данные хранятся на страницах, и SQL обычно должен изменить данные только на одной странице в файле данных, чтобы вставить строку, возможно, с обновленными парой других страниц, но это не похоже на вставку строки в массивный текстовый файл.

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

person Chris Shaffer    schedule 26.04.2011
comment
То, что вы сказали, верно и пропорционально объему данных, хранящихся в таблице с использованием основного GUID. Но по моему опыту, это ресурсоемкий (согласно SQL Profiler) в таблицах с большим объемом данных (2 миллиона +). Я предпочитаю сам использовать INT, делая первичным идентификатор. Нет ежу понятно, потому что новые записи всегда добавляются ... поэтому SQL не нужно перестраивать указатели индекса. Итак, вопрос в том, должен ли я использовать собственный провайдер с моей собственной схемой? Никогда не думайте, что у Microsoft есть все правильные решения (извините, Джоэл)... - person dotnettex; 26.04.2011
comment
Я бы, наверное, сказал, проверьте все, что вы рассматриваете. Однако в качестве небольшого примечания: с 2 миллионами строк в таблице гиды будут занимать примерно 24 МБ дополнительного пространства (16 байтов на запись вместо 4 байтов на запись), и в реальности большинство баз данных не заканчиваются 2 миллионами. учетные записи пользователей. Теперь, если вы создаете какие-либо таблицы, которые включают guid в качестве внешнего ключа, у вас могут возникнуть проблемы; Но обязательно измерьте свою проблему и стоимость ее решения, прежде чем зайти слишком далеко. - person Chris Shaffer; 26.04.2011
comment
Одним из решений, которые я использовал в прошлом (признаюсь, что я не измерял характеристики производительности), было использование таблицы членства по умолчанию И моей собственной пользовательской таблицы. В моей пользовательской таблице был первичный ключ int, который использовался во всех других таблицах БД в качестве внешнего ключа, а также ссылка внешнего ключа guid на пользовательскую таблицу членства; При входе в систему я просмотрел свою запись в пользовательской таблице и сохранил идентификатор int в сеансе (который затем использовался во всем приложении). Это позволило мне использовать стандартный код членства без (частично предполагаемой) утечки guids, засоряющей мою схему БД. - person Chris Shaffer; 26.04.2011

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

Каждый из них советовал мне держаться подальше от GUID в качестве первичных ключей при построении базы данных... потому что первичный ключ — это кластеризованный индекс!

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

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

Ниже приведен сценарий SQL из сценария InstallCommon.sql в %WINDIR%\Microsoft.NET\Framework\v4.0.30319:

  CREATE TABLE [dbo].aspnet_Users (
    ApplicationId    uniqueidentifier    NOT NULL FOREIGN KEY REFERENCES [dbo].aspnet_Applications(ApplicationId),
    UserId           uniqueidentifier    NOT NULL PRIMARY KEY NONCLUSTERED DEFAULT NEWID(),
    UserName         nvarchar(256)       NOT NULL,
    LoweredUserName  nvarchar(256)       NOT NULL,
    MobileAlias      nvarchar(16)        DEFAULT NULL,
    IsAnonymous      bit                 NOT NULL DEFAULT 0,
    LastActivityDate DATETIME            NOT NULL)

   CREATE UNIQUE CLUSTERED INDEX aspnet_Users_Index ON [dbo].aspnet_Users(ApplicationId, LoweredUserName)
   CREATE NONCLUSTERED INDEX aspnet_Users_Index2 ON [dbo].aspnet_Users(ApplicationId, LastActivityDate)

Обратите внимание, что столбец первичного ключа (UserId) создается с помощью инструкции PRIMARY KEY NONCLUSTERED, а индекс таблицы CLUSTERED создается как составной индекс по ApplicationId и LoweredUserName.

person Scott Mitchell    schedule 26.04.2011
comment
Разве идентификатор строки не должен обрабатывать дубликаты в ситуации синхронизации? Я знаю, что вы можете выбрать NON-CLUSTERED, однако CLUSTERED индекс обеспечивает наилучшую производительность в таблице с большим объемом данных. В любом случае, я просто буду плыть по течению и решу проблему, если это станет проблемой. Спасибо за ваш вклад, ребята!! Всегда приятно узнавать что-то новое! - person dotnettex; 26.04.2011
comment
@dotnettex: если первичный ключ представляет собой целое число, вы рискуете столкнуться с конфликтами при репликации данных из баз данных магазинов в корпоративную базу данных. И таблица aspnet_Users (в качестве примера) имеет КЛАСТЕРНЫЙ индекс - он находится на ApplicationId и LoweredUsername. Это имеет смысл, так как часто вы будете искать по имени пользователя определенное приложение (например, Membership.GetUser()). - person Scott Mitchell; 26.04.2011