Почему последовательности Denali должны работать лучше, чем идентичные столбцы?

36

В своем ответе « Что лучше»: столбцы идентификаторов или сгенерированные уникальные значения идентификаторов? Мрденни говорит:

Когда выйдет SQL Denali, он будет поддерживать последовательности, которые будут более эффективными, чем идентичность, но вы не сможете создать что-то более эффективное самостоятельно.

Я не совсем уверен. Зная последовательности Oracle , я должен либо создать триггер для вставки, инкапсулировать каждую вставку в вызов хранимой процедуры, либо молиться, чтобы я не забыл правильно использовать последовательность, когда выполняю вставку ad-hoc.

Я сомневаюсь, что преимущества последовательностей настолько очевидны.

bernd_k
источник
2
Я знаю, что это не отвечает на ваш вопрос, но кроме различий в производительности, у последовательностей есть и другие преимущества. Например, последовательность не мешает вам обновить целевой столбец, что является очень неудобным ограничением IDENTITY.
nvogel

Ответы:

37

Я также отвечу здесь. Это связано с внутренностями того, как IDENTITYи как SEQUENCEработать.

С помощью IDENTITYSQL Server предварительно кэширует значения в памяти, чтобы они были легко доступны. Смотрите ответ Мартина Смита для деталей. Поскольку используются значения, фоновый процесс генерирует больше значений. Как вы можете себе представить, этот пул может закончиться довольно быстро, оставив приложение во власти фонового процесса, который генерирует значения.

С помощью SEQUENCESQL Server вы можете определить размер кэша. Хотя SQL Server на самом деле не хранит значения в кэше, он сохраняет только текущее значение и верхнее значение, это значительно уменьшит количество операций ввода-вывода, необходимых для создания значений.

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

Что касается вставки строки, просто укажите значение по умолчанию для столбца, например так:

DEFAULT (NEXT VALUE FOR Audit.EventCounter),
mrdenny
источник
21

С тех пор, как была написана статья Ицик Бен Ган, жестко закодированный размер кэша 10, IDENTITYпохоже, изменился. Из комментариев к этому элементу подключения

Размер предварительного выделения основан на размере типа данных столбца, для которого определено свойство идентификатора. Для целочисленного столбца SQL Server сервер предварительно выделяет идентификаторы в диапазонах 1000 значений. Для типа данных bigint сервер предварительно выделяет в диапазоне 10000 значений.

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

+-----------------+-----------+
|    DataType     | CacheSize |
+-----------------+-----------+
| TinyInt         | 10        |
| SmallInt        | 100       |
| Int             | 1,000     |
| BigInt, Numeric | 10,000    |
+-----------------+-----------+

В этой статье мы проверяем различные размеры кэша последовательностей и размеры пакетов вставок и приводим следующие результаты.

введите описание изображения здесь

Который, кажется, показывает, что для больших вставок IDENTITYвыполняет SEQUENCE. Тем не менее, он не проверяет размер кеша 1000, и эти результаты - только один тест. Рассматривая конкретно размер кэша 1000 с различными размерами пакетов вставок, я получил следующие результаты (пробуя каждый размер пакета 50 раз и агрегируя результаты как показано ниже - все разы в мкс).

+------------+-----------+-----------+-----------+-----------+-----------+-----------+
|            |             Sequence              |             Identity              |
| Batch Size |    Min    |    Max    |    Avg    |    Min    |    Max    |    Avg    |
+------------+-----------+-----------+-----------+-----------+-----------+-----------+
| 10         | 2,994     | 7,004     | 4,002     | 3,001     | 7,005     | 4,022     |
| 100        | 3,997     | 5,005     | 4,218     | 4,001     | 5,010     | 4,238     |
| 1,000      | 6,001     | 19,013    | 7,221     | 5,982     | 8,006     | 6,709     |
| 10,000     | 26,999    | 33,022    | 28,645    | 24,015    | 34,022    | 26,114    |
| 100,000    | 189,126   | 293,340   | 205,968   | 165,109   | 234,156   | 173,391   |
| 1,000,000  | 2,208,952 | 2,344,689 | 2,269,297 | 2,058,377 | 2,191,465 | 2,098,552 |
+------------+-----------+-----------+-----------+-----------+-----------+-----------+

Для больших размеров партии IDENTITYверсия кажется вообще более быстрой .

Книга запросов TSQL также объясняет, почему IDENTITYможет иметь преимущество в производительности над последовательностью.

IDENTITYСтоловое специфичны и SEQUENCEне является. Если произошла авария в середине вставки до того, как буфер журнала был сброшен, не имеет значения, является ли восстановленная идентификация более ранней, поскольку процесс восстановления также отменяет вставку, поэтому SQL Server не принудительно сбрасывает буфер журнала при каждой идентификации запись диска на кеш. Однако для последовательности это будет исполнено , поскольку значение может быть использовано для любых целей - в том числе и за пределами базы данных. Таким образом, в приведенном выше примере с миллионом вставок и размером кэша в 1000 это дополнительные тысячи сбросов журнала.

Скрипт для воспроизведения

DECLARE @Results TABLE(
  BatchCounter INT,
  NumRows      INT,
  SequenceTime BIGINT,
  IdTime       BIGINT);

DECLARE @NumRows      INT = 10,
        @BatchCounter INT;

WHILE @NumRows <= 1000000
  BEGIN
      SET @BatchCounter = 0;

      WHILE @BatchCounter <= 50
        BEGIN
            --Do inserts using Sequence
            DECLARE @SequenceTimeStart DATETIME2(7) = SYSUTCDATETIME();

            INSERT INTO dbo.t1_Seq1_cache_1000
                        (c1)
            SELECT N
            FROM   [dbo].[TallyTable] (@NumRows)
            OPTION (RECOMPILE);

            DECLARE @SequenceTimeEnd DATETIME2(7) = SYSUTCDATETIME();
            --Do inserts using IDENTITY
            DECLARE @IdTimeStart DATETIME2(7) = SYSUTCDATETIME();

            INSERT INTO dbo.t1_identity
                        (c1)
            SELECT N
            FROM   [dbo].[TallyTable] (@NumRows)
            OPTION (RECOMPILE);

            DECLARE @IdTimeEnd DATETIME2(7) = SYSUTCDATETIME();

            INSERT INTO @Results
            SELECT @BatchCounter,
                   @NumRows,
                   DATEDIFF(MICROSECOND, @SequenceTimeStart, @SequenceTimeEnd) AS SequenceTime,
                   DATEDIFF(MICROSECOND, @IdTimeStart, @IdTimeEnd)             AS IdTime;

            TRUNCATE TABLE dbo.t1_identity;

            TRUNCATE TABLE dbo.t1_Seq1_cache_1000;

            SET @BatchCounter +=1;
        END

      SET @NumRows *= 10;
  END

SELECT NumRows,
       MIN(SequenceTime) AS MinSequenceTime,
       MAX(SequenceTime) AS MaxSequenceTime,
       AVG(SequenceTime) AS AvgSequenceTime,
       MIN(IdTime)       AS MinIdentityTime,
       MAX(IdTime)       AS MaxIdentityTime,
       AVG(IdTime)       AS AvgIdentityTime
FROM   @Results
GROUP  BY NumRows;
Мартин Смит
источник