Последовательный Guid и фрагментация
Я пытаюсь понять, как последовательный гид работает лучше, чем обычный гид.
Это потому, что при обычном guid для сортировки индекс использует последний байт guid? Поскольку это случайно, это вызовет много фрагментации и расщепление страницы, так как это часто будет перемещать данные на другую страницу, чтобы вставить новые данные?
Последовательный guid sine это последовательно, это вызовет намного меньше разбиений страницы и фрагментации?
Правильно ли мое понимание?
Если кто-то может пролить больше света на эту тему, я буду очень признателен.
Спасибо
РЕДАКТИРОВАТЬ:
Последовательный guid = NEWSEQUENTIALID(),
Обычный гид = NEWID()
3 ответа
Вы в значительной степени сказали все это в своем вопросе.
С помощью последовательного идентификатора GUID / первичного ключа новые строки будут добавляться вместе в конце таблицы, что упрощает работу SQL-сервера. Для сравнения, случайный первичный ключ означает, что новые записи могут быть вставлены в любое место таблицы - вероятность того, что последняя страница таблицы будет в кеше, весьма вероятна (если именно там происходят все операции чтения), однако вероятность случайная страница в середине таблицы, находящаяся в кеше, довольно мала, а это означает, что требуется дополнительный ввод-вывод.
Кроме того, при вставке строк в середину таблицы существует вероятность, что для вставки дополнительной строки недостаточно места. Если это так, то SQL-серверу необходимо выполнить дополнительные дорогостоящие операции ввода-вывода, чтобы освободить место для записи. Единственный способ избежать этого - разбросать пропуски между данными, чтобы можно было вставить дополнительные записи (известные как Коэффициент заполнения), который сам по себе вызывает проблемы с производительностью, поскольку данные распределяются по большему количеству страниц и поэтому для доступа ко всей таблице требуется больше ввода-вывода.
Я полагаюсь на мудрость Кимберли Л. Триппа в этой теме:
Но GUID, который не является последовательным - как тот, у которого есть значения, сгенерированные в клиенте (с использованием.NET) ИЛИ сгенерированные функцией newid() (в SQL Server), может быть ужасно плохим выбором - прежде всего из-за фрагментации, которая он создается в базовой таблице, но также из-за его размера. Он неоправданно широк (он в 4 раза шире, чем основанная на int идентичность, что может дать вам 2 миллиарда (на самом деле, 4 миллиарда) уникальных строк). И, если вам нужно более 2 миллиардов, вы всегда можете использовать bigint (8-байтовое int) и получить 263-1 ряд.
Узнайте больше: http://www.sqlskills.com/BLOGS/KIMBERLY/post/GUIDs-as-PRIMARY-KEYs-andor-the-clustering-key.aspx
Для визуализации всей картины можно использовать имя ostress. Например, вы можете создать две таблицы: одну с обычным GUID в качестве PK, другую с последовательным GUID:
-- normal one
CREATE TABLE dbo.YourTable(
[id] [uniqueidentifier] NOT NULL,
CONSTRAINT [PK_YourTable] PRIMARY KEY NONCLUSTERED (id)
);
-- sequential one
CREATE TABLE dbo.YourTableSeq(
[id] [uniqueidentifier] NOT NULL CONSTRAINT [df_yourtable_id] DEFAULT (newsequentialid()),
CONSTRAINT [PK_YourTableSeq] PRIMARY KEY NONCLUSTERED (id)
);
Затем с помощью данного утилиты вы запускаете число вставок с выбором статистики о фрагментации индекса:
ostress -Slocalhost -E -dYourDB -Q"INSERT INTO dbo.YourTable VALUES (NEWID()); SELECT count(*) AS Cnt FROM dbo.YourTable; SELECT AVG_FRAGMENTATION_IN_PERCENT AS AvgPageFragmentation, PAGE_COUNT AS PageCounts FROM sys.dm_db_index_physical_stats (DB_ID(), NULL, NULL , NULL, N'LIMITED') DPS INNER JOIN sysindexes SI ON DPS.OBJECT_ID = SI.ID AND DPS.INDEX_ID = SI.INDID WHERE SI.NAME = 'PK_YourTable';" -oE:\incoming\TMP\ -n1 -r10000
ostress -Slocalhost -E -dYourDB -Q"INSERT INTO dbo.YourTableSeq DEFAULT VALUES; SELECT count(*) AS Cnt FROM dbo.YourTableSeq; SELECT AVG_FRAGMENTATION_IN_PERCENT AS AvgPageFragmentation, PAGE_COUNT AS PageCounts FROM sys.dm_db_index_physical_stats (DB_ID(), NULL, NULL , NULL, N'LIMITED') DPS INNER JOIN sysindexes SI ON DPS.OBJECT_ID = SI.ID AND DPS.INDEX_ID = SI.INDID WHERE SI.NAME = 'PK_YourTableSeq';" -oE:\incoming\TMP\ -n1 -r10000
Затем в файле E:\ Входящий \TMP\query.out вы найдете свою статистику. Мои результаты:
"Normal" GUID:
Records AvgPageFragmentation PageCounts
----------------------------------------------
1000 87.5 8
2000 93.75 16
3000 96.15384615384616 26
4000 96.875 32
5000 96.969696969696969 33
10000 98.571428571428584 70
Sequential GUID:
Records AvgPageFragmentation PageCounts
----------------------------------------------
1000 83.333333333333343 6
2000 63.636363636363633 11
3000 41.17647058823529 17
4000 31.818181818181817 22
5000 25.0 28
10000 12.727272727272727 55
Как вы можете видеть при вставке последовательно сгенерированного GUID, индекс гораздо менее фрагментирован, поскольку операция вставки приводит к более редкому выделению страниц.