Задав этот вопрос, сравнивая последовательные и непоследовательные GUID, я попытался сравнить производительность INSERT на 1) таблице с первичным ключом GUID, инициализируемой последовательно с newsequentialid()
, и 2) таблице с первичным ключом INT, инициализированной последовательно с identity(1,1)
. Я ожидал бы, что последний будет самым быстрым из-за меньшей ширины целых чисел, и также кажется более простым генерировать последовательное целое число, чем последовательный GUID. Но, к моему удивлению, INSERT в таблице с целочисленным ключом были значительно медленнее, чем в последовательной таблице GUID.
Это показывает среднее время использования (мс) для тестовых прогонов:
NEWSEQUENTIALID() 1977
IDENTITY() 2223
Кто-нибудь может объяснить это?
Был использован следующий эксперимент:
SET NOCOUNT ON
CREATE TABLE TestGuid2 (Id UNIQUEIDENTIFIER NOT NULL DEFAULT NEWSEQUENTIALID() PRIMARY KEY,
SomeDate DATETIME, batchNumber BIGINT, FILLER CHAR(100))
CREATE TABLE TestInt (Id Int NOT NULL identity(1,1) PRIMARY KEY,
SomeDate DATETIME, batchNumber BIGINT, FILLER CHAR(100))
DECLARE @BatchCounter INT = 1
DECLARE @Numrows INT = 100000
WHILE (@BatchCounter <= 20)
BEGIN
BEGIN TRAN
DECLARE @LocalCounter INT = 0
WHILE (@LocalCounter <= @NumRows)
BEGIN
INSERT TestGuid2 (SomeDate,batchNumber) VALUES (GETDATE(),@BatchCounter)
SET @LocalCounter +=1
END
SET @LocalCounter = 0
WHILE (@LocalCounter <= @NumRows)
BEGIN
INSERT TestInt (SomeDate,batchNumber) VALUES (GETDATE(),@BatchCounter)
SET @LocalCounter +=1
END
SET @BatchCounter +=1
COMMIT
END
DBCC showcontig ('TestGuid2') WITH tableresults
DBCC showcontig ('TestInt') WITH tableresults
SELECT batchNumber,DATEDIFF(ms,MIN(SomeDate),MAX(SomeDate)) AS [NEWSEQUENTIALID()]
FROM TestGuid2
GROUP BY batchNumber
SELECT batchNumber,DATEDIFF(ms,MIN(SomeDate),MAX(SomeDate)) AS [IDENTITY()]
FROM TestInt
GROUP BY batchNumber
DROP TABLE TestGuid2
DROP TABLE TestInt
ОБНОВЛЕНИЕ: модифицируя скрипт для выполнения вставок на основе таблицы TEMP, как в примерах Фила Сэндлера, Митча Уитта и Мартина ниже, я также обнаружил, что IDENTITY быстрее, чем должно быть. Но это не обычный способ вставки строк, и я до сих пор не понимаю, почему эксперимент поначалу не удался: даже если я опускаю GETDATE () из моего исходного примера, IDENTITY () все еще намного медленнее. Таким образом, кажется, что единственный способ сделать IDENTITY () превосходящим NEWSEQUENTIALID () - это подготовить строки для вставки во временную таблицу и выполнить множество вставок в виде пакетной вставки с использованием этой временной таблицы. В общем, я не думаю, что мы нашли объяснение этому феномену, а IDENTITY () все еще медленнее для большинства практических применений. Кто-нибудь может объяснить это?
источник
INT IDENTITY
IDENTITY
не требует блокировки таблицы. Концептуально я мог видеть, что вы могли бы ожидать, что он принимает MAX (id) + 1, но в действительности следующее значение сохраняется. На самом деле это должно быть быстрее, чем найти следующий GUID.Ответы:
Я изменил код @Phil Sandler, чтобы удалить эффект вызова GETDATE () (могут быть аппаратные эффекты / прерывания?), И сделал строки одинаковой длины.
[Начиная с SQL Server 2000 было несколько статей, касающихся проблем синхронизации и таймеров с высоким разрешением, поэтому я хотел минимизировать этот эффект.]
В простой модели восстановления с данными и файлом журнала оба размера по сравнению с тем, что требуется, вот время (в секундах): (Обновлены с новыми результатами, основанными на точном коде ниже)
Используемый код:
Прочитав расследование @ Мартина, я перезапустил предложенный TOP (@num) в обоих случаях, т.е.
и вот результаты синхронизации:
Я не смог получить реальный план выполнения, так как запрос не вернулся! Кажется, ошибка возможна. (Запуск Microsoft SQL Server 2008 R2 (окончательная первоначальная версия) - 10.50.1600.1 (X64))
источник
SORT
оператор для GUID?NEWSEQUENTIALID
любом случае. Это сделает индекс глубже, будет использовать на 20% больше страниц данных в случае OP, и гарантированно будет только увеличиваться, пока машина не будет перезагружена, поэтому имеет много недостатков по сравнению сidentity
. В этом случае просто кажется, что план запроса добавляет еще один ненужный план!В новой базе данных в простой модели восстановления с размером файла данных 1 ГБ и размером файла журнала 3 ГБ (ноутбук, оба файла на одном диске) и интервалом восстановления 100 минут (чтобы избежать искажения результатов контрольной точки), я вижу аналогичные результаты для вас с одной строкой
inserts
.Я проверил три случая: для каждого случая я сделал 20 серий вставки 100 000 строк по отдельности в следующие таблицы. Полные сценарии можно найти в истории изменений этого ответа .
Для третьей таблицы тест вставил строки с инкрементным
Id
значением, но это было вычислено самостоятельно путем увеличения значения переменной в цикле.Усреднение времени по 20 партиям дало следующие результаты.
Вывод
Так что это определенно
identity
связано с процессом создания, который отвечает за результаты. Для инкрементного инкремента, вычисляемого самостоятельно, тогда результаты будут намного более точными, чем можно было бы ожидать при рассмотрении только стоимости ввода-вывода.Когда я помещаю код вставки, описанный выше, в хранимые процедуры и проверяю,
sys.dm_exec_procedure_stats
он дает следующие результатыТаким образом, в этих результатах
total_worker_time
примерно на 30% выше. Это представляетТаким образом, просто кажется, что код, который генерирует
IDENTITY
значение, является более интенсивным ЦП, чем тот, который генерируетNEWSEQUENTIALID()
(разница между двумя цифрами составляет 10231308, что в среднем составляет около 5 мкс на вставку.) И что для этого определения таблицы это фиксированная стоимость ЦП было достаточно высоким, чтобы перевесить дополнительные логические операции чтения и записи, вызванные большей шириной ключа. (Примечание: Ицик Бен Ган провел подобное тестирование здесь и обнаружил штраф в 2 мкс за каждую вставку)Так почему же
IDENTITY
процессор интенсивнееUuidCreateSequential
?Я считаю, что это объясняется в этой статье . Для каждого десятого
identity
сгенерированного значения SQL Server должен записать изменение в системные таблицы на дискеА как насчет MultiRow Inserts?
Когда 100 000 строк были вставлены в одно утверждение, я обнаружил, что разница исчезла,
GUID
хотя, возможно, незначительная выгода для случая, но далеко не так очевидна. Среднее значение для 20 партий в моем тесте былоПричина, по которой в коде Фила и в первом наборе результатов Митча нет такого наказания, заключается в том, что так получилось, что код, который я использовал для многострочной вставки, использовался
SELECT TOP (@NumRows)
. Это не позволило оптимизатору правильно оценить количество строк, которые будут вставлены.Похоже, что это полезно, поскольку есть определенный переломный момент, когда он добавит дополнительную операцию сортировки для (предположительно, последовательных!)
GUID
.Эта операция сортировки не требуется из пояснительного текста в BOL .
Так что мне показалось ошибкой или отсутствующей оптимизацией, что SQL Server не распознает, что выходные данные вычислимого скаляра уже будут предварительно отсортированы, как это, очевидно, уже делает для
identity
столбца. ( Изменить Я сообщил об этом, и проблема с ненужной сортировкой теперь исправлена в Denali )источник
Все очень просто: с GUID дешевле генерировать следующий номер в строке, чем для IDENTITY (текущее значение GUID хранить не нужно, IDENTITY должно быть). Это верно даже для NEWSEQUENTIALGUID.
Вы можете сделать тест более справедливым и использовать SEQUENCER с большим кэшем, который дешевле, чем IDENTITY.
Но, как говорит MR, у GUID есть несколько основных преимуществ. На самом деле, они НАМНОГО более масштабируемы, чем столбцы IDENTITY (но только если они НЕ последовательные).
Смотрите: http://blog.kejser.org/2011/10/05/boosting-insert-speed-by-generating-scalable-keys/
источник
IDENTITY
. отсюда жалобыЯ очарован этим типом вопроса. Почему вы должны были опубликовать это в пятницу вечером? :)
Я думаю, что даже если ваш тест предназначен ТОЛЬКО для измерения производительности INSERT, вы (возможно) ввели ряд факторов, которые могут вводить в заблуждение (циклы, длительные транзакции и т. Д.)
Я не совсем уверен, что моя версия что-то доказывает, но идентичность работает лучше, чем в ней GUID (3,2 секунды против 6,8 секунды на домашнем ПК):
источник
Я запускал ваш пример сценария несколько раз, внося несколько изменений в счетчик и размер партии (и большое спасибо за его предоставление).
Сначала я скажу, что вы измеряете только один аспект производительности клавиш -
INSERT
скорость. Так что, если вы не заинтересованы только в том, чтобы как можно быстрее получить данные в таблицы, у этого животного гораздо больше.Мои выводы были в целом похожи на ваши. Тем не менее, я хотел бы отметить , что вариации в
INSERT
скорости междуGUID
иIDENTITY
(INT) немного больше , сGUID
чемIDENTITY
- может быть +/- 10% между запусками. Количество используемых партийIDENTITY
варьировалось менее чем на 2–3% каждый раз.Также отметим, что мой тестовый блок явно менее мощный, чем ваш, поэтому мне пришлось использовать меньшее количество строк.
источник
Я собираюсь вернуться к другой статье о стеке потока для этой же темы - https://stackoverflow.com/questions/170346/what-are-the-performance-improvement-of-sequential-guid-over-standard-guid
Одна вещь, которую я знаю, это то, что наличие последовательных идентификаторов GUID заключается в том, что использование индекса лучше благодаря очень небольшому движению листа и, следовательно, сокращению поиска по HD. Я думаю, что из-за этого вставки будут быстрее, так как не нужно распределять ключи по большому количеству страниц.
Мой личный опыт показывает, что при реализации большой базы данных с большим трафиком лучше использовать GUID, потому что это делает ее гораздо более масштабируемой для интеграции с другими системами. Это касается, в частности, репликации и ограничений int / bigint ... не то, что у вас закончатся bigints, но в конечном итоге вы это сделаете и вернетесь назад.
источник