8 байтов для метки времени или 6 байтов для метки времени в COMB GUID на SQLServer

Благодаря замечательной статье Стоимость GUID в качестве первичных ключей , у нас есть COMB GUID. Исходя из текущей реализации, существует 2 подхода:

  1. используйте последние 6 байтов для отметки времени: GUID как быстрые первичные ключи в нескольких базах данных
  2. используйте последние 8 байтов для метки времени с помощью тика Windows: Стратегия GUID COMB в EF4.1 (CodeFirst)

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

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

Прежде чем мы продолжим, сделаем короткую сноску об этом подходе: использование временной метки с разрешением 1 миллисекунда означает, что GUID, сгенерированные очень близко друг к другу, могут иметь одинаковое значение временной метки, и поэтому не будут последовательными. Это может быть обычным явлением для некоторых приложений, и на самом деле я экспериментировал с некоторыми альтернативными подходами, такими как использование таймера с более высоким разрешением, такого как System.Diagnostics.Stopwatch, или объединение отметки времени со «счетчиком», который гарантировал бы последовательность продолжалось до обновления метки времени. Однако во время тестирования я обнаружил, что это вообще не имело заметной разницы, даже когда десятки или даже сотни GUID генерировались в одном и том же окне длительностью в одну миллисекунду. Это согласуется с тем, с чем столкнулся Джимми Нильссон во время тестирования COMB.

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

Обновление: на основании нашего тестирования, COMB GUID не смог уменьшить фрагментацию таблицы, поскольку это заявлено через Интернет, по сравнению со случайным GUID. Кажется, что сейчас единственный способ - использовать SQL Server для генерации последовательного GUID.


person windfly2006    schedule 06.03.2014    source источник
comment
Я думаю, что все перечисленные статьи путают первичный ключ с ключом кластеризованного индекса. GUIDS хорошо работают как первичные ключи, особенно в ситуациях с несколькими главными серверами, но не работают как ключи кластеризованного индекса (хотя и не работают, зависит от того, какие еще столбцы есть в таблице).   -  person Greenstone Walker    schedule 11.03.2014
comment
Да, ты прав. Наша основная проблема связана с тем, что это вызовет большую фрагментацию из-за случайности GUID, который является кластеризованным PK за нашим столом. Есть ли какие-либо мысли по поводу моих вопросов выше для выполнения случайного GUID с той же меткой времени?   -  person windfly2006    schedule 12.03.2014
comment
Если нет другого столбца для использования в качестве ключа кластеризованного индекса, я бы выбрал newsequentialid() (из @ErikE ниже).   -  person Greenstone Walker    schedule 14.03.2014


Ответы (1)


Статья, на которую вы ссылаетесь, датирована 2002 годом и очень старая. Просто используйте newsequentialid (доступно в SQL Server 2005 и выше). Это гарантирует, что каждый новый идентификатор, который вы создаете, больше предыдущего, что решает проблему фрагментации индекса / разделения страниц.

Однако еще один аспект, о котором я хотел бы упомянуть, который автор этой статьи умалчивает, заключается в том, что использование 16 байтов, когда вам нужно только 4, не является хорошей идеей. Предположим, у вас есть таблица с 500000 строк, в среднем 150 байтов, не включая кластеризованный столбец, и таблица имеет 3 некластеризованных индекса (которые повторяют кластеризованный столбец в каждой строке), каждый по очереди со строками в среднем 4 байта, 25 байтов и 50 байтов, не считая кластеризованного столбца.

Таким образом, требования к хранилищу при идеальном коэффициенте заполнения 100% (все числа в мегабайтах, кроме%):

Item  Clust  50     25     4      Total
----  -----  -----  -----  -----  ------
GUID  79.1   31.5   19.6    9.5   139.7
 int  73.4   25.7   13.8    3.8   116.7
%imp   7.2%  18.4%  29.6%  60.0%   16.5%

В некластеризованном индексе, имеющем только один int столбец размером 4 байта (общий сценарий), переключение кластеризованного индекса на int делает его на 60% меньше! Это приводит к увеличению производительности на 60% при любом сканировании таблицы - и это консервативно, потому что с меньшими строками разделение страниц будет происходить реже, а фрагментация будет дольше оставаться лучше.

Даже в самом кластеризованном индексе производительность улучшилась на 7,2%, что вовсе не пустяк.

Что, если бы вы использовали GUIDs во всей своей базе данных, в которой были таблицы с таким же профилем, как эта, где переключение на int привело бы к уменьшению размера на 16,5%, а сама база данных была размером 1,397 терабайта? Вся ваша база данных будет на 230 ГБ больше (см. Столбец «Всего», 139,7–116,7). В реальном мире это превращается в реальные деньги за высокодоступное хранилище. Он сдвигает ваш график покупки дисков раньше во времени, что наносит ущерб чистой прибыли вашей компании.

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

ОБНОВЛЕНИЕ

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

Сначала установите значение по умолчанию для столбца CustomerID:

ALTER TABLE dbo.Customer ADD CONSTRAINT DF_Customer_CustomerID
   DEFAULT (newsequentialid()) FOR Customer;

Теперь вам не нужно указывать, какое значение вставлять для CustomerID в любой INSERT, и ваш запрос может выглядеть так:

DECLARE @Name varchar(100) = 'Acme Spy Devices';
INSERT dbo.Customer (Name)
OUTPUT inserted.CustomerID -- a GUID
VALUES (@Name);

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

Если вы хотите явно вставить VALUES (newsequentialid(), @Name), это тоже сработает.

person ErikE    schedule 07.03.2014
comment
большое спасибо. Мы используем Entity Framework, и мы используем код для генерации GUID для PK, поэтому мы ищем некоторые минимальные изменения кода для генерации последовательных GUID в коде. На этом этапе мы не сможем вернуться к INT как PK из-за слишком большого изменения кода. Если мы используем newsequentialid, то для генерации этого GUID потребуется дополнительный обход базы данных, верно? - person windfly2006; 08.03.2014
comment
Правильно, newsequentialid() нужно было бы вызвать из базы данных. Иногда код может быть переписан, чтобы получить идентификатор после вставки всей записи вместо того, чтобы получать идентификатор раньше - если да, то без дополнительных обращений к базе данных. - person ErikE; 08.03.2014
comment
Еще раз спасибо. Просто задайтесь вопросом, не могли бы вы поделиться какой-нибудь ссылкой или образцом кода на случай, когда дополнительная поездка в базу данных не требуется. - person windfly2006; 10.03.2014
comment
При расчете дискового пространства не учитывается свободное пространство. - person Greenstone Walker; 11.03.2014
comment
Да, @GreenstoneWalker, это было намеренно, чтобы не давать точного учета фактического использования пространства (обратите внимание, я сказал идеальный коэффициент заполнения 100%), просто чтобы показать разумное сравнение между требованиями к пространству для двух столбцов. Как вы ожидаете, что учет незаполненного пространства повлияет на ситуацию? Как вы думаете, повлияет ли это на мое утверждение о том, что использование 16 байтов вместо 4 - плохой выбор дизайна? - person ErikE; 11.03.2014