Приріст ідентичності стрибає в базі даних SQL Server


126

В одній із моїх таблиць Feeу стовпці "ReceiptNo" у базі даних SQL Server 2012 приріст ідентичності бази даних раптово почав стрибати до 100s замість 1, залежно від двох наступних речей.

  1. якщо це 1205446, це стрибки до 1206306, якщо це 1206321, то він переходить до 1207306, а якщо це 1207314, то стрибає до 1208306. Що я хочу зауважити, це те, що останні три цифри залишаються постійними, тобто 306 щоразу, коли стрибки відбувається, як показано на наступному малюнку.

  2. ця проблема виникає під час перезавантаження комп'ютера

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


Якщо ви додасте order by ReceiptNoдо запиту, чи справді таких записів немає? Ви впевнені, що при вставці записів немає помилок? Якщо запис намагається вставити і не вдасться, ідентифікація збільшиться, те саме, якщо записи буде видалено. Якщо записи видалено ReceiptNo, не скидається. Чи можете ви опублікувати таблицю створення Feeтаблиці?
Taryn

4
Перше питання - чому це важливо? це має бути довільний унікальний ідентифікатор
Андрій

1
Це працює на сервері чи це, можливо, виражається на робочому столі? Цікаво, чому здається, що служба так часто запускається?
Мартін Сміт

@bluefeet Я знаю, коли виникає помилка, відбувається збільшення особи. Я на 100% впевнений, що помилок немає. Я редагую своє запитання, додавши таблицю та збережену процедуру, яку я використовую для вставки рядків.
kashif

@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розподіл записаним як у версіях до R2 2008 року. Це поширюється у всьому світі на всі бази даних.
  3. Або для останніх версій виконайте, ALTER DATABASE SCOPED CONFIGURATION SET IDENTITY_CACHE = OFFщоб відключити кешування ідентичності для певної бази даних.

Вам слід пам’ятати, що жоден із цих обхідних шляхів не забезпечить прогалин. Це ніколи не гарантувалося тим IDENTITY, що це було б можливо лише шляхом серіалізації вкладок до столу. Якщо вам потрібен стовпчик без змін, вам потрібно буде використовувати інше рішення, ніж IDENTITYабоSEQUENCE


1
щоб перевірити те, що ви сказали, я вставив деякі значення і отримав 1208309, 1208310, а потім перезапустив сервер, а потім, коли я додав рядок, я отримав 1209309, це означає, що ви сказали, що абсолютно правильно. дуже дякую. тепер ви можете сказати мені, як я можу вирішити цю проблему. Ви б запропонували мені використовувати sql сервер 2008 замість 2012, який я раніше використовував або навіть використовував 2012, цю проблему можна вирішити ??
kashif

1
@kashif - це насправді проблема для вас? Навіть якщо ви використовуєте до 1000 значень ідентичності на день, все одно знадобиться 2 мільйони днів, перш ніж у вас закінчуються значення. Якщо ви хочете старої поведінки, ви можете встановити SQL Server для запуску з прапором 272 сліду, або ви можете використовувати a SEQUENCEзамість IDENTITYта встановити послідовність, щоб розмір кешу був 0.
Мартін Сміт

Я отримав дуже вражаючі та задовільні відповіді від вас за своє рішення завдяки багато.
kashif

Насправді з вашого, CREATE TABLEя бачу, ви використовуєте, numeric(7)і ви почали нумерацію, 1200001це означає, що ви закінчитесь через 8,799дні (24 роки), якщо використовуєте 1000 на день.
Мартін Сміт

Фактичне значення "стрибку" повинно залежати від використовуваного типу стовпця. Наприклад, big intстовпець зазвичай "стрибає" на 10000 за перезапуск.
StarPilot

60

Ця проблема виникає після перезавантаження SQL Server.

Рішення таке:

  • Запустіть менеджер конфігурацій SQL Server .

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

    Менеджер конфігурацій SQL Server

  • Клацніть правою кнопкою миші SQL Server і виберіть Властивості .

  • У вікні, що відкривається в розділі Параметри запуску , введіть -T272та натисніть кнопку Додати , потім натисніть кнопку Застосувати та перезапустіть.

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


1
Цей метод справді працює, велике спасибі! і як сказано тут , ця проблема не буде виправлена ​​в SQL Server 2012, а в її пакетах послуг - лише в наступній версії версії.
Фрагмент

2
Чи є спосіб застосувати прапор трасування до окремих баз даних? Я не хочу вносити ці зміни на весь сервер, оскільки у мене є сторонні бази даних, і я не впевнений, як це вплине на них.
Еге Ерсос

1
Я не дотримувався причин, але, мабуть, деяким користувачам довелося використовувати малі "t", щоб змусити його працювати. Дивіться посилання, розміщене Fragment, у коментарі вище.
Дикун

33

З SQL Server 2017+можна використовувати ALTER DATABASE контекстні конфігурації :

IDENTITY_CACHE = {ON | OFF}

Вмикає або вимикає кеш ідентичності на рівні бази даних. За замовчуванням увімкнено. Кешування ідентичності використовується для поліпшення продуктивності INSERT на таблицях із стовпцями Identity. Щоб уникнути прогалин у значеннях стовпця Identity у випадках, коли сервер несподівано перезапустився або перейшов на вторинний сервер, відключіть опцію IDENTITY_CACHE. Цей параметр схожий на існуючий прапор трасування SQL Server 272, за винятком того, що його можна встановити на рівні бази даних, а не лише на рівні сервера.

(...)

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

Потім додайте його до запуску, використовуючи наступний синтаксис.

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

Це гарна ідея, якщо у вас є кілька таблиць. але якщо вам доведеться робити для багатьох таблиць, цей спосіб все ще працює, але не є хорошою ідеєю.


Гарна ідея. Але це не працює для залежних таблиць, чи не так? Я маю на увазі, чи це фіксує зовнішні ключові значення?
rom5jp

@ rom5jp Виправлення FK не є суть цієї відповіді. Це все про виправлення можливого наступного ПК значення таблиці. Поки MAX (id) не знаходиться в жодному з FK, він повинен працювати.
Джеяра

14

Це все ще дуже поширена проблема серед багатьох розробників та додатків незалежно від розміру.

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

Крім того, якщо у вас є існуючі таблиці, які використовують ці стовпчики ідентичності для первинних ключів, це величезні зусилля, щоб скинути ці стовпці і відтворити нові, щоб використовувати обхід послідовності BS. Вирішення послідовності корисне лише в тому випадку, якщо ви розробляєте нові таблиці з нуля в SQL 2012+

Суть полягає в тому, що якщо ви перебуваєте на сервері Sql 2008R2, то ОБЕРЕЖУЙТЕ НА ньому. Серйозно, залишайся на цьому. Поки Microsoft не визнає, що вони запровадили ВЕЛИЧУЮ помилку, яка все ще є навіть у Sql Server 2016, тоді ми не повинні оновлювати, поки вони не володіють нею та виправлять її.

Майкрософт прямо ввів переломну зміну, тобто вони зламали робочий API, який більше не працює так, як було розроблено, через те, що їх система забуває свою поточну ідентичність при перезапуску. Кеш або немає кешу, це неприпустимо, і розробник Microsoft за іменем Брайана повинен володіти ним, а не казати світові, що це "за задумом" і "особливістю". Звичайно, кешування - це особливість, але втрата сліду, якою має бути наступна ідентичність, НЕ ОСОБЛИВО. Це відчайдушний БУГ !!!

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

Натомість це мій ганебний злом (але не такий ганебний, як ця помилка POS, яку ввів мікрософт).

Злому / виправлення:

Перед командами вставки просто перезавантажте свою особу перед кожною вставкою. Це виправлення рекомендується лише в тому випадку, якщо ви не маєте адміністраторського контролю над вашим екземпляром сервера Sql, інакше я пропоную повторно перезапустити сервер.

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 рядки безпосередньо перед вставкою, і вам слід добре пройти. Це дійсно не вплине на продуктивність настільки сильно, тобто буде непомітно.

Удачі.


7

Існує багато можливих причин стрибків значень ідентичності. Вони варіюються від відкатних вставок до управління ідентифікацією для реплікації. Що викликає це у вашому випадку, я не можу сказати, не витративши трохи часу у вашій системі.

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

Ви можете знайти трохи більше інформації про це тут: http://sqlity.net/en/792/the-gap-in-the-identity-value-sequence/

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.