Мы работаем над веб-приложением, пока недоступным для пользователей. Мой начальник заметил, что вновь созданные записи получают идентификатор более 10 000, даже если в таблице всего менее 100 записей. Она предположила, что веб-интерфейс по какой-то причине создает в 100 раз больше временных записей, чем фактические (и удаляет их), и это может привести к исчерпанию диапазона в течение нескольких месяцев после выпуска.
Я не думаю, что она права относительно причины инфляции ID (коллега, который может ответить на это, находится в отпуске, поэтому мы не знаем наверняка), но давайте предположим, что это так. Она сказала, что не хотела бы использовать столбец bigint и хотела бы, чтобы мы прекратили автоинкрементирование столбца ID и писали код на стороне сервера, который выбирает первое «неиспользуемое» целое число и использует его в качестве идентификатора.
Я аспирант по информатике с небольшим практическим опытом, занимающий младшую должность разработчика. Она имеет многолетний опыт управления всеми базами данных нашей организации и разработки большинства из них. Я думаю, что она неверна в этом случае, что bigint ID нечего бояться, и что имитация функциональности СУБД пахнет антипаттерном. Но я пока не доверяю своему суждению.
Каковы аргументы за и против каждой позиции? Какие плохие вещи могут случиться, если мы используем bigint, и каковы опасности переосмысления функции автоинкрементации колеса ? Есть ли третье решение, которое лучше, чем одно? Каковы могут быть ее причины для того, чтобы избежать инфляции по номиналу? Мне также интересно услышать о прагматических причинах - может быть, идентификаторы bigint работают в теории, но на практике вызывают головные боли?
Приложение не должно обрабатывать очень большие объемы данных. Я сомневаюсь, что он достигнет 10 000 реальных записей в течение следующих нескольких лет.
Если это имеет какое-то значение, мы используем сервер Microsoft SQL. Приложение написано на C # и использует Linq to SQL.
Обновить
Спасибо, я нашел существующие ответы и комментарии интересными. Но я боюсь, что вы неправильно поняли мой вопрос, поэтому они содержат то, что я хотел знать.
Меня не очень беспокоит реальная причина высоких идентификаторов. Если мы не сможем найти его самостоятельно, я мог бы задать другой вопрос. Что меня интересует, так это понять процесс принятия решений в этом случае. Для этого, пожалуйста, предположите, что приложение будет писать 1000 записей в день, а затем удалит 9999 из них . Я почти уверен, что это не так, но это то, во что верил мой босс, когда она сделала свой запрос. Итак, в этих гипотетических обстоятельствах, каковы будут плюсы и минусы использования bigint или написания нашего собственного кода, который будет присваивать идентификаторы (таким образом, чтобы повторно использовать идентификаторы уже удаленных записей, чтобы гарантировать отсутствие пробелов)?
Что касается фактической причины, я сильно подозреваю, что это потому, что мы когда-то писали код для импорта данных из другой базы данных, в качестве доказательства концепции, что более поздняя миграция может быть выполнена в определенной степени. Я думаю, что мой коллега на самом деле создал несколько тысяч записей во время импорта, а затем удалил их. Я должен подтвердить, так ли это на самом деле, но если это так, то даже не нужно предпринимать какие-либо действия.
источник
Ответы:
Не видя код, довольно сложно окончательно сказать, что происходит. Хотя, скорее всего,
IDENTITY
значение кэшируется, что приводит к появлению пробелов в значении после перезапуска SQL Server. См. Https://stackoverflow.com/questions/17587094/identity-column-value-suddenly-jumps-to-1001-in-sql-server для некоторых хороших ответов и информации об этом.Простое
INT
поле может содержать значения до 2 147 483 647. На самом деле значение идентификатора можно начать с -2 147 483 648, что дает полные 32 бита значений. 4 миллиарда разных значений. Я очень сомневаюсь, что у вас кончатся ценности для использования. Предполагая , что ваше приложение будет потреблять 1000 значений для каждой строки фактической добавленной, вам необходимо создать около 12 000 строк в день каждый день , чтобы бежать из идентификаторов в течение 6 месяцев , предполагающих вы началиIDENTITY
значение 0, и были с помощью INT. Если бы вы использовали BIGINT, вам пришлось бы подождать 21 миллион веков, прежде чем исчерпали бы значения, если вы написали 12 000 строк в день, потребляя 1000 «значений» на строку.Сказав все это, если вы хотите использовать
BIGINT
в качестве типа данных поля идентификатора, в этом нет ничего плохого. Это даст вам для всех намерений и целей неограниченный запас ценностей для использования. Разница в производительности между INT и BIGINT практически отсутствует на современном 64-разрядном оборудовании и является более предпочтительной, чем, например,NEWID()
для генерации идентификаторов GUID.Если вы хотите управлять своими собственными значениями для столбца ID, вы можете создать таблицу ключей и предоставить довольно пуленепробиваемый способ сделать это, используя один из методов, показанных в ответах на этот вопрос: обработка одновременного доступа к таблице ключей без тупики в SQL Server
Другой вариант, если вы используете SQL Server 2012+, - это использование
SEQUENCE
объекта для получения значений идентификатора для столбца. Однако вам необходимо настроить последовательность так, чтобы она не кэшировала значения. Например:В ответ на негативное восприятие вашего босса «больших» чисел, я бы сказал, какая разница? Предполагая, что вы используете
INT
поле, сIDENTITY
, вы можете фактически начатьIDENTITY
at2147483647
и "увеличить" значение на-1
. Это не имеет абсолютно никакого значения для потребления памяти, производительности или используемого дискового пространства, поскольку 32-разрядное число составляет 4 байта, независимо от того, является ли оно0
или2147483647
.0
в двоичном виде -00000000000000000000000000000000
при хранении в 32-битномINT
поле со знаком.2147483647
является01111111111111111111111111111111
- оба числа занимают одинаковое количество места, как в памяти, так и на диске, и оба требуют точно одинакового количества операций процессора для обработки. Гораздо важнее правильно составить код приложения, чем помнить о фактическом числе, хранящемся в ключевом поле.Вы спрашивали о плюсах и минусах: (а) использования столбца идентификаторов с большей емкостью, например
BIGINT
, или (б) развертывания собственного решения для предотвращения пропусков идентификаторов. Чтобы ответить на эти вопросы:BIGINT
а неINT
как тип данных для рассматриваемого столбца. Использование aBIGINT
требует удвоенного объема памяти, как на диске, так и в памяти для самого столбца. Если столбец является индексом первичного ключа для используемой таблицы, каждый некластеризованный индекс, присоединенный к таблице, также будет хранитьBIGINT
значение, в два раза большеINT
, и снова, как в памяти, так и на диске. SQL Server хранит данные на диске в страницах размером 8 КБ, где количество «строк» на «страницу» зависит от «ширины» каждой строки. Так, например, если у вас есть таблица с 10 столбцами, каждый изINT
которых может содержать примерно 160 строк на страницу. Если эти столбцы, где вместоBIGINT
столбцы, вы сможете хранить только 80 строк на странице. Для таблицы с очень большим количеством строк это явно означает, что число операций ввода-вывода, необходимых для чтения и записи таблицы, в этом примере будет удвоенным для любого заданного числа строк. Конечно, это довольно экстремальный пример - если бы у вас была строка, состоящая из одного столбцаINT
илиBIGINT
столбца и одногоNCHAR(4000)
столбца, вы (упрощенно) получили бы одну строку на страницу, независимо от того, использовали ли выINT
или aBIGINT
. В этом сценарии это не будет иметь большого значения.Прокручивая свой собственный сценарий, чтобы избежать пробелов в столбце ID. Вам нужно было бы написать свой код таким образом, чтобы определение значения «следующего» идентификатора для использования не конфликтовало с другими действиями, происходящими с таблицей. Что-то в духе
SELECT TOP(1) [ID] FROM [schema].[table]
наивного приходит на ум. Что если несколько актеров пытаются одновременно записать новые строки в таблицу? Два актера могут легко получить одно и то же значение, что приведет к конфликту записи. Чтобы обойти эту проблему, требуется сериализовать доступ к таблице, снижая производительность. Было написано много статей по этой проблеме; Я оставлю это для читателя, чтобы выполнить поиск по этой теме.Отсюда вывод: вам нужно понять ваши требования и правильно оценить как количество строк, так и ширину строк, а также требования к параллелизму вашего приложения. Как обычно, это зависит ™.
источник
bigint
вам , вероятно , поблагодарите себя за решение , что заранее , а не необходимости добавить это к таблице с миллиардами строк.Основная задача - найти основную причину, по которой текущее значение так высоко.
Наиболее разумным объяснением версий SQL Server до SQL2012 (при условии, что вы говорите о тестовой базе данных) было бы то, что был проведен нагрузочный тест с последующей очисткой.
Начиная с SQL2012, наиболее вероятной причиной является несколько перезапусков SQL Engine (как объяснено в первой ссылке, предоставленной Максом).
Если разрыв вызван тестовым сценарием, нет причин для беспокойства с моей точки зрения. Но чтобы быть в безопасности, я бы проверял значения идентификаторов во время нормального использования приложения, а также до и после перезапуска двигателя.
Забавно, что MS заявляет, что обе альтернативы (либо флаг трассировки 272, либо новый объект SEQUENCE) могут повлиять на производительность.
Это может быть лучшим решением использовать BIGINT вместо INT, чтобы быть в безопасности, чтобы покрыть MS следующие "улучшения" ...
источник
Rumtscho, Если вы создаете только 1000 строк в день, вам остается только принять решение - используйте тип данных INT с полем Identity и покончите с этим. Простая математика говорит, что если вы дадите своему приложению 30-летний жизненный цикл (маловероятно), вы можете иметь 200 000 строк в день и при этом оставаться в диапазоне положительных чисел типа данных INT.
Использование BigInt в вашем случае является излишним, оно также может вызвать проблемы, если ваше приложение или данные будут доступны через ODBC (например, перенесены в Excel или MS Access и т. Д.), Bigint плохо переводит большинство драйверов ODBC в настольные приложения.
Что касается GUIDS, помимо дополнительного дискового пространства и дополнительного ввода-вывода, существует огромная проблема, заключающаяся в том, что они по своей структуре не последовательны, поэтому, если они являются частью отсортированного индекса, можно догадаться, что каждая вставка будет требует, чтобы индекс был восстановлен. --Джим
источник
Есть ли разрыв между используемыми значениями? Или начальные значения 10.000 и с этого момента все добавляют 1? Иногда, если число будет присвоено клиентам, начальное число будет больше нуля, например, скажем, 1500, поэтому клиент не осознает, что система «новая».
Недостаток использования bigint вместо smallint состоит в том, что, поскольку bigint использует «больше дискового пространства», при чтении диска вы читаете меньше дисковых блоков для каждого диска. Если ваше пространство строк мало, то это может быть недостатком, если нет, то это не имеет большого значения. Также не имеет большого значения, если вы не запрашиваете много ресурсов одновременно, и если у вас есть правильные индексы.
И, как сказано в другом ответе, если вы беспокоитесь о нехватке индексов, тогда вам не стоит беспокоиться, smallint может справиться, если у вас нет миллионера. Изобретать механизм «восстановления идентификаторов» стоит дорого, он добавляет точки сбоя и усложняет программное обеспечение.
С уважением
источник
Если бы я был вашим боссом, меня больше всего интересовали бы причины неожиданно высоких значений Id ... как я понимаю, для каждого из двух описанных вами сценариев:
Если предварительное тестирование увеличило значения идентификаторов - тогда ваши другие комментарии об ожидаемом количестве записей также подтолкнули бы меня к предложению меньшего типа ключа. Честно говоря, я бы также подумал, возможно ли сбросить последовательность и перенумеровать существующие записи, если тест был не в своем роде для текущего предполагаемого использования таблицы (большинство сочло бы это перебором - «это зависит»).
Если большинство записей, записанных в таблицу, будут удалены вскоре после того, как я склонен рассмотреть возможность использования двух таблиц; временная таблица, в которой записи не хранятся в течение длительного времени, и другая, в которой хранятся только записи, которые мы будем создавать постоянно. Опять же, ваши ожидания относительно количества долгосрочных записей подсказывают мне использование меньшего типа для ключевого столбца, и несколько записей в день вряд ли вызовут у вас проблему с производительностью, чтобы «переместить» запись из одной таблицы в другую подобным образом. один. Я подозреваю, что это не ваш сценарий, но представьте, что веб-сайт покупок может предпочесть сохранить Basket / BasketItem, и когда заказ фактически размещен, данные перемещаются в набор Order / OrderItem.
Обобщить; на мой взгляд, BIGINT не обязательно бояться, но, откровенно говоря, излишне велики для многих сценариев. Если таблица никогда не становится большой, вы никогда не поймете, что при выборе типа было слишком много ... но если у вас есть таблицы с миллионами строк и множеством столбцов FK, которые являются БОЛЬШИМИ, когда они могли бы быть меньше - тогда вы можете пожелать типы были выбраны более консервативно (учитывайте не только ключевые столбцы, но и все ключевые столбцы, все резервные копии, которые вы храните и т. д.). Дисковое пространство не всегда дешевое (рассмотрим SAN-диск в управляемых местах - то есть дисковое пространство арендуется).
По сути, я настаиваю на тщательном рассмотрении вашего выбора типа данных всегда, а не иногда . Вы не всегда будете правильно прогнозировать модели использования, но я думаю, что вы, как правило, будете принимать лучшие решения, чем всегда предполагать, что «чем больше, тем лучше». В общем, я выбираю наименьший тип, который может содержать требуемый и разумный диапазон значений, и я с удовольствием рассмотрю INT, SMALLINT и даже TINYINT, если я думаю, что значение, вероятно, будет соответствовать этому типу в обозримом будущем. Однако малые типы вряд ли будут использоваться со столбцами IDENTITY, но могут успешно использоваться с таблицами поиска, в которых значения ключей задаются вручную.
Наконец, технологии, которые люди используют, могут значительно повлиять на их ожидания и ответы. Некоторые инструменты с большей вероятностью могут вызвать разрывы в диапазонах, например, путем предварительного бронирования диапазонов идентификаторов для каждого процесса. Напротив, @DocSalvager предлагает тщательную проверяемую последовательность, которая, кажется, отражает точку зрения вашего босса; Лично я никогда не требовал такого уровня полномочий, хотя общее правило, согласно которому идентичности являются последовательными и обычно без пробелов, часто было невероятно полезным для меня в ситуациях поддержки и анализа проблем.
источник
Используя
bigint
как личность и живя с пробелами:int
все равно даст вам данные за 2 миллиона дней; больше страниц нужно будет прочитать и написать; индексы могут стать глубже. (В этих объемах это не является серьезной проблемой, однако).Сверните свои собственные:
источник
Если вы действительно заинтересованы в достижении верхнего порога INT для ваших PK, рассмотрите возможность использования GUID. Да, я знаю, что это 16 байтов против 4 байтов, но диск дешевый.
Вот хорошее описание плюсов и минусов.
источник
Первичные ключи СУБД (столбец обычно называется «ИД»)
Нельзя избежать пробелов в автоинкрементных столбцах (полях) СУБД. Они в первую очередь предназначены для создания уникальных ПК. Для повышения производительности основные продукты распределяют их по частям, поэтому механизмы автоматического восстановления для различных сбоев нормальной работы могут привести к тому, что числа останутся неиспользованными. Это нормально.
Непрерывные последовательности Если вам нужен непрерывный порядковый номер, такой, который часто ожидают пользователи, это должен быть отдельный столбец, который назначается программно и не должен быть PK. Таким образом, все эти 1000 записей могут иметь одинаковый номер в этом столбце.
Почему пользователи хотят непрерывных последовательностей?
Пропущенные порядковые номера являются основным признаком ошибки, обнаруживаемой при любом виде аудита. Этот принцип "Бухгалтерия-101" повсеместен. Однако то, что работает для небольшого количества записей, поддерживаемых вручную, имеет серьезную проблему применительно к очень большому количеству записей в базах данных ...
Повторное использование значений ключей для несвязанных записей делает базу данных недействительной.
Использование «первого неиспользуемого целого числа» повышает вероятность того, что в какой-то момент в будущем число будет повторно использовано для записей, не связанных с оригиналом. Это делает базу данных ненадежной как точное представление фактов. Это основная причина, по которой механизмы автоинкрементации специально разработаны для того, чтобы никогда не использовать значение повторно.
источник