Приращение идентичности прыгает в базе данных SQL Server

126

В одной из моих таблиц Feeв столбце «ReceiptNo» в SQL Server 2012 приращение идентификатора базы данных внезапно начало прыгать до 100 с вместо 1 в зависимости от следующих двух вещей.

  1. если это 1205446, он переходит на 1206306, если это 1206321, он переходит на 1207306, а если это 1207314, он переходит на 1208306. Я хочу отметить, что последние три цифры остаются постоянными, то есть 306 при каждом прыжке происходит, как показано на следующем рисунке.

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

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

Kashif
источник
Если вы добавите order by ReceiptNoв свой запрос, действительно ли этих записей нет? Вы уверены, что при вставке записей ошибок нет? Если запись пытается вставить, но не удается, идентификатор будет увеличиваться, то же самое при удалении записей. Если записи удалены ReceiptNo, сброс не выполняется. Можете ли вы разместить таблицу создания для Feeтаблицы?
Тарин
4
Первый вопрос - почему это важно? это должен быть произвольный уникальный идентификатор
Эндрю
1
Это работает на сервере или, возможно, на рабочем столе? Хотите знать, почему кажется, что служба перезапускается так часто?
Мартин Смит
@bluefeet Я знаю, когда возникает ошибка, происходит приращение идентичности. Я на 100% уверен, что ошибок нет. Я редактирую свой вопрос, добавляя таблицу и хранимую процедуру, которую я использую для вставки строк.
kashif 03
@kashif - на 99% уверен, что в этом нет необходимости. Прыгает ровно 1000 ( 1206306, 1207306, 1207806) означает объяснение в Connect Item Thread почти наверняка относится.
Мартин Смит

Ответы:

158

Вы сталкиваетесь с таким поведением из-за улучшения производительности после SQL Server 2012.

Теперь он по умолчанию использует размер кэша 1000 при распределении IDENTITYзначений для intстолбца, и перезапуск службы может «потерять» неиспользуемые значения (размер кеша составляет 10 000 для bigint/ numeric).

Об этом упоминается в документации

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

Судя по показанным вами данным, похоже, что это произошло после ввода данных за 22 декабря, а затем при перезапуске SQL Server зарезервировал значения 1206306 - 1207305. После ввода данных за 24–25 декабря был произведен еще один перезапуск, и SQL Server зарезервировал следующий диапазон, 1207306 - 1208305видимый в записях, для 28 числа.

Если вы не перезапускаете службу с необычной частотой, любые «потерянные» значения вряд ли существенно повлияют на диапазон значений, разрешенный типом данных, поэтому лучшая политика - не беспокоиться об этом.

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

  1. Вы можете использовать SEQUENCEвместо столбца идентификаторов и, например, определить меньший размер кеша и использовать его NEXT VALUE FORв столбце по умолчанию.
  2. Или примените флаг трассировки 272, который IDENTITYзаписывает выделение, как в версиях до 2008 R2. Это относится ко всем базам данных глобально.
  3. Или, в последних версиях, выполнить, ALTER DATABASE SCOPED CONFIGURATION SET IDENTITY_CACHE = OFFчтобы отключить кеширование идентификаторов для конкретной базы данных.

Вы должны знать, что ни один из этих обходных путей не гарантирует отсутствие пробелов. Это никогда не было гарантировано, IDENTITYпоскольку это было бы возможно только путем сериализации вставок в таблицу. Если вам нужен столбец без зазоров, вам нужно будет использовать другое решение, чем либо, IDENTITYлибоSEQUENCE

Мартин Смит
источник
1
Чтобы проверить, что вы сказали, я вставил некоторые значения и получил 1208309, 1208310, а затем перезапустил сервер, а затем, когда я добавил строку, я получил 1209309, что означает, что вы сказали абсолютно правильно. большое спасибо. Подскажите, пожалуйста, как я могу решить эту проблему. не могли бы вы предложить мне использовать sql server 2008 вместо 2012, который я использовал ранее или даже использую 2012, эта проблема может быть решена ??
kashif 03
1
@kashif - Это действительно проблема для вас? Даже если вы используете до 1000 значений идентификаторов в день, все равно пройдет 2 миллиона дней, прежде чем значения закончатся. Если вам все-таки нужно старое поведение, вы можете настроить SQL Server на запуск с флагом трассировки 272 или вы можете использовать SEQUENCEвместо a IDENTITYи установить для Sequence размер кэша 0.
Мартин Смит
Я получил от вас довольно впечатляющие и удовлетворительные ответы на мое решение, большое спасибо.
kashif 03
На самом деле, CREATE TABLEя вижу, что вы используете numeric(7)и начали нумерацию, 1200001это означает, что вы закончите через 8,799несколько дней (24 года), если будете использовать 1000 в день.
Мартин Смит
Предполагается, что фактическое значение "перескочило" зависит от типа используемого столбца. Например, big intстолбец обычно "прыгает" на 10 000 за перезапуск.
StarPilot
60

Эта проблема возникает после перезапуска SQL Server.

Решение такое:

  • Запустите диспетчер конфигурации SQL Server .

  • Выберите Службы SQL Server .

    Диспетчер конфигурации SQL Server

  • Щелкните правой кнопкой мыши SQL Server и выберите Свойства .

  • В открывшемся окне в разделе « Параметры запуска» введите -T272и нажмите « Добавить» , затем нажмите кнопку « Применить» и перезапустите.

    Параметры запуска SQL Server

Харун ЭРГУЛЬ
источник
1
Этот метод действительно работает, спасибо большое! и, как здесь сказано , эта проблема не будет исправлена ​​в SQL Server 2012 и в его пакетах обновления - только в следующем выпуске версии.
Фрагмент
2
Есть ли способ применить флаг трассировки к отдельным базам данных? Я не хочу вносить это изменение на весь сервер, потому что у меня есть сторонние базы данных, и я не уверен, как это повлияет на них.
Ege Ersoz 07
1
Я не следил за причинами, но, видимо, некоторым пользователям приходилось использовать строчную букву «t», чтобы заставить его работать. См. Ссылку, опубликованную Fragment в комментарии выше.
Savage
33

Из SQL Server 2017+можно использовать ALTER DATABASE контекстные КОНФИГУРАЦИИ :

IDENTITY_CACHE = {ВКЛ | ВЫКЛ}

Включает или отключает кэш идентификации на уровне базы данных. По умолчанию включено. Кэширование идентификаторов используется для повышения производительности INSERT для таблиц со столбцами идентификаторов. Чтобы избежать пропусков в значениях столбца Identity в случаях, когда сервер неожиданно перезагружается или переключается на вторичный сервер, отключите параметр IDENTITY_CACHE. Этот параметр аналогичен существующему флагу трассировки 272 SQL Server, за исключением того, что его можно установить на уровне базы данных, а не только на уровне сервера.

(...)

G. Установить IDENTITY_CACHE

В этом примере отключается кеш идентификации.

ALTER DATABASE SCOPED CONFIGURATION SET IDENTITY_CACHE=OFF ;
Лукаш Шозда
источник
25

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

Создайте следующую хранимую процедуру в основной БД.

USE [master]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO

ALTER PROCEDURE [dbo].[ResetTableNameIdentityAfterRestart]
AS
BEGIN

begin TRAN
    declare @id int = 0
    SELECT @id =  MAX(id) FROM [DatabaseName].dbo.[TableName]
    --print @id
    DBCC CHECKIDENT ('[DatabaseName].dbo.[TableName]', reseed, @id)
Commit

END

Затем добавьте его в Start up, используя следующий синтаксис.

EXEC sp_procoption 'ResetOrderIdentityAfterRestart', 'startup', 'on';

Это хорошая идея, если у вас мало столов. но если вам нужно сделать для многих таблиц, этот метод все равно работает, но не очень хорошая идея.

Jeyara
источник
Хорошая идея. Но для зависимых таблиц это не работает? Я имею в виду, исправляет ли он значения внешнего ключа?
rom5jp 01
@ rom5jp Исправление FK не является целью этого ответа. Все дело в исправлении возможного следующего значения PK таблицы. Пока MAX (id) не входит ни в один из FK, он должен работать.
Jeyara 02
14

Это по-прежнему очень распространенная проблема среди многих разработчиков и приложений независимо от размера.

К сожалению, приведенные выше предложения не исправляют все сценарии, например, виртуальный хостинг, вы не можете полагаться на свой хост при установке параметра запуска -t272.

Кроме того, если у вас есть существующие таблицы, которые используют эти столбцы идентификаторов для первичных ключей, это ОГРОМНОЕ усилие, чтобы удалить эти столбцы и воссоздать новые, чтобы использовать обходной путь последовательности BS. Обходной путь Sequence хорош только в том случае, если вы создаете новые таблицы с нуля в SQL 2012+.

Суть в том, что если вы используете Sql Server 2008R2, оставайтесь на нем. Серьезно, оставайся на этом. Пока Microsoft не признает, что они представили ОГРОМНУЮ ошибку, которая все еще существует даже в Sql Server 2016, мы не должны обновляться до тех пор, пока они не станут ее владельцем и не ИСПРАВЛЯЮТ ИТ.

Microsoft сразу же внесла критическое изменение, то есть они сломали рабочий API, который больше не работает так, как задумано, из-за того, что их система забывает их текущую идентификацию при перезапуске. Кэширование или отсутствие кеша - это неприемлемо, и разработчик Microsoft по имени Брайан должен владеть им, вместо того, чтобы говорить миру, что это «по замыслу» и «функция». Конечно, кэширование - это функция, но потеря информации о том, какой должна быть следующая личность, НЕ ЯВЛЯЕТСЯ ФУНКЦИЕЙ. Это чертова ОШИБКА !!!

Я поделюсь обходным путем, который я использовал, потому что мои БД находятся на серверах общего хостинга, а также я не удаляю и не воссоздаю свои столбцы первичного ключа, что было бы огромным PITA.

Вместо этого это мой постыдный хакер (но не такой постыдный, как эта ошибка POS, которую представила Microsoft).

Hack / Fix:

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

declare @newId int -- where int is the datatype of your PKey or Id column
select @newId = max(YourBuggedIdColumn) from YOUR_TABLE_NAME
DBCC CheckIdent('YOUR_TABLE_NAME', RESEED, @newId)

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

Удачи.

green_mystic_Orb
источник
7

Есть много возможных причин для скачка ценностей идентичности. Они варьируются от отката вставок до управления идентификацией для репликации. Что вызывает это в вашем случае, я не могу сказать, не потратив некоторое время на вашу систему.

Однако вы должны знать, что ни в коем случае нельзя предполагать, что столбец идентификаторов является смежным. Слишком много вещей может вызвать пробелы.

Вы можете найти немного больше информации об этом здесь: http://sqlity.net/en/792/the-gap-in-the-identity-value-sequence/

Себастьян Майне
источник