Вставка не удалась, но значение идентичности растет, нарушает ли это правило атомарности?

Когда я импортирую данные в новую таблицу из большого Excel, если одна запись не удалась, то ничего не импортируется. Я думаю, что это нормально, потому что это соответствует правилу атомарности. Однако, когда я исправил ошибку исходных данных и снова импортировал, столбец идентификаторов не начинается с 1, а начинается с большого значения.

Например

create table #test (id int identity(1,1), name varchar(4) default '')

insert into #test (name) values('1 insert will failed');
select ident_current('#test') as ident_current
insert into #test (name) values('2 insert will failed');
select ident_current('#test') as ident_current

insert into #test (name) values('3 OK');
select ident_current('#test') as ident_current

select * from #test

drop table #test

Результат

id          name 
----------- ---- 
3           3 OK

Википедия описывает ACID следующим образом.

атомарность

Атомарность требует, чтобы каждая транзакция была «все или ничего»: если одна часть транзакции завершается сбоем, вся транзакция завершается с ошибкой, а состояние базы данных остается неизменным. Атомарная система должна гарантировать атомарность в любой ситуации, включая сбои питания, ошибки и сбои.

Итак, похоже, что SQL Server не оставляет состояние базы данных (значение идентификатора) неизменным, если вставка не удалась, так что нарушает ли это правило ACID?

Кстати, PostgreSQL не позволяет увеличивать значение идентификатора (серийного номера) при сбое вставки. (Обновление: только иногда, см. комментарии. НЕ полагайтесь на это.).

test=# create table AutoIncrementTest (id serial not null, name varchar(4));
NOTICE:  CREATE TABLE will create implicit sequence "autoincrementtest_id_seq" for serial column "autoincrementtest.id"
CREATE TABLE
test=# insert into autoincrementtest(name) values('12345');
ERROR:  value too long for type character varying(4)
test=# insert into autoincrementtest(name) values('12345');
ERROR:  value too long for type character varying(4)
test=# insert into autoincrementtest(name) values('1234');
INSERT 0 1
test=# select * from autoincrementtest;
 id | name
----+------
  1 | 1234

person LiuYan 刘研    schedule 19.07.2012    source источник
comment
хм, что происходит в PostgreSQL, когда две вставки происходят одновременно, а первая откатывается?   -  person Conrad Frix    schedule 19.07.2012
comment
ПОСЛЕДОВАТЕЛЬНОСТИ в PostgreSQL также не связаны правилами атомарности. Это происходит только здесь, потому что новое значение не проходит проверку прежде чем INSERT должен получить значение последовательности. Если у вас также было ограничение UNIQUE для столбца имени, а затем вы попытались дважды вставить одно и то же имя (вторая вставка не удалась), а затем вставить другое имя, вы должны получить две записи, но с промежутком между идентификационными номерами. Это сделано из соображений производительности. Если у вас ДОЛЖНЫ быть последовательности без пропусков, вы будете платить штраф за производительность, так как каждый INSERT должен будет получить эксклюзивную блокировку счетчика последовательности.   -  person Matthew Wood    schedule 19.07.2012
comment
@MatthewWood это похоже на ПОСЛЕДОВАТЕЛЬНОСТИ в SQL Server 2012.   -  person Aaron Bertrand    schedule 19.07.2012
comment
@MatthewWood, спасибо за объяснение последовательности PostgreSQL. Я сделал тест, и разрыв действительно существовал. ALTER TABLE AutoIncrementTest ADD CONSTRAINT UQ_Name UNIQUE (name); insert into autoincrementtest(name) values('123'); insert into autoincrementtest(name) values('123'); insert into autoincrementtest(name) values('1233');, дает следующий результат: id=2,name=123; id=4,name=1233   -  person LiuYan 刘研    schedule 19.07.2012
comment
Также все должны знать о эта ошибка в SQL Server 2012, которая может привести к неожиданным пробелам. И нет, ошибка не в том, есть пробелы, поскольку IDENTITY не гарантирует отсутствие пробелов, а в том, что пробелы больше, чем ожидалось.   -  person Aaron Bertrand    schedule 19.07.2012
comment
Просто чтобы прояснить комментарии @MatthewWood, вы не можете использовать SEQUENCE, если вам нужны последовательности без пробелов в PostgreSQL, потому что некоторые сбои возникают только во время фиксации (например, сбои сериализации). Вы должны использовать обычную таблицу с одной строкой, где вы SELECT ... FOR UPDATE или UPDATE ... RETURNING значение. Это заблокирует выполнение других транзакций после этой точки до тех пор, пока транзакция с блокировкой не зафиксирует или не откатится, поэтому убедитесь, что транзакции, основанные на беспрерывных последовательностях, действительно короткие. Если возможно, исправьте свой дизайн, чтобы он не нуждался в бесконечных последовательностях.   -  person Craig Ringer    schedule 20.07.2012


Ответы (3)


Поскольку значение идентификатора не является чем-то, что физически хранится в какой-либо части базы данных, к которой вы можете получить доступ, я не согласен с тем, что это нарушает атомарность. Если вы не хотите «нарушать атомарность» или если вы заботитесь о пробелах (вы не должны), есть другие способы сделать это (например, использовать сериализуемую транзакцию и взять MAX(col)+1 для новой строки ).

person Aaron Bertrand    schedule 19.07.2012
comment
Вы можете получить доступ к значению с помощью DBCC CHECKIDENT и, очевидно, должно быть физически сохранено на сервере где-то msdn.microsoft.com/en-us/library/ms176057.aspx - person Jodrell; 19.07.2012
comment
@Jodrell да, он хранится, но, пожалуйста, дайте мне знать, где его можно найти без использования системных функций. Я также могу выполнять сценарии xp_cmdshell и xp_reg из базы данных, делает ли это файловую систему и реестр частью атомарности? - person Aaron Bertrand; 19.07.2012
comment
Я не верю, что столбцы идентификаторов являются частью стандартного SQL, поэтому это будет сложно. В любом случае, я согласен с тем, что вы не должны полагаться на смежные значения, и поэтому атомарность является спорной. - person Jodrell; 19.07.2012
comment
@Jodrell Другими словами, сгенерированное значение идентификатора не является частью ваших данных до тех пор, пока оно не будет успешно зафиксировано в вашей таблице. Значение IDENTITY — это бессмысленный суррогатный ключ. Если вы сгенерируете значение NEWID() и ничего с ним не сделаете, ожидаете ли вы, что оно будет записано где-то, чтобы оно стало следующим, используемым какой-то другой транзакцией, или создание другого значения NEWID() также нарушит атомарность? Как насчет того, чтобы попытаться откатить вызов sp_send_dbmail или NET SEND? Вы должны рассматривать генерацию значений IDENTITY как внешнюю во всех смыслах и целях. - person Aaron Bertrand; 19.07.2012
comment
Я согласен с вами, но с пуристической точки зрения все ваши примеры нарушили бы атомарность. Я не предлагаю, чтобы это изменилось. Лучше, чтобы MS SQL был расширен таким образом без бремени какой-то системы распределенных транзакций. - person Jodrell; 19.07.2012
comment
Может я какой-то параноик, пробелы давали почувствовать, что данные грязные. :С - person LiuYan 刘研; 19.07.2012
comment
@LiuYan刘研, ПРЕКРАТИТЕ использовать столбцы IDENTITY. Либо используйте гораздо более дорогой и менее параллельный метод, как я предложил, либо используйте более широкий ключ (например, GUID), где пробелы не имеют значения, либо пересмотрите, почему вы считаете, что они грязные. Имеют ли значение фактические значения? Почему? Какой смысл вы вкладываете в ценности? - person Aaron Bertrand; 19.07.2012
comment
Что ж, я часто рассматриваю значение идентификатора как номер строки записи (например, номер версии системы контроля версий), но если возникают пробелы (особенно большие), то я не могу сказать, сколько записей было вставлено, когда я видел последний ряд. Кроме того, зазор может ограничивать общее количество записей, я не могу вставить 65535 записей, если тип столбца идентификаторов smallint (короткий), а в столбце идентификаторов имеется пробел в 10000 значений, если только я не использую значение в зазоре повторно. - person LiuYan 刘研; 19.07.2012
comment
@LiuYan刘研 тогда, как я уже сказал, перестаньте использовать IDENTITY для этого. - person Aaron Bertrand; 19.07.2012
comment
MAX(col)+1 можно использовать программно, но при использовании инструментов импорта и экспорта я не могу контролировать, чтобы значение столбца было MAX(col)+1. Так что немного жаль, если не использовать IDENTITY. :( - person LiuYan 刘研; 19.07.2012
comment
@LiuYan刘研, поскольку SQL Server работает не так, как вы хотите, вам нужно сделать выбор, извините. - person Aaron Bertrand; 19.07.2012
comment
@ Джодрелл, но это не удовлетворит требованию. Это показывает последовательность без пробелов, но не указывает, когда строка отсутствует. - person Aaron Bertrand; 20.07.2012

Да, так что не полагайтесь на смежные значения с MSSQL Server.

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

Я предполагаю, что это отклонение от строгого соответствия ACID позволяет оптимизировать производительность в MS SQL Server.

person Jodrell    schedule 19.07.2012
comment
Не уверен, что отклонение может оптимизировать производительность, но я думаю, что у SQL Server должны быть некоторые причины для этого. - person LiuYan 刘研; 19.07.2012
comment
@LiuYan刘研 причина именно в производительности. SQL Server распределяет следующее значение и не заботится о том, будет ли оно в конечном итоге использовано. Таким образом, следующему пользователю не нужно ждать, чтобы увидеть, было ли использовано предыдущее значение или нет, прежде чем определить, каким должно быть следующее следующее значение. В высококонкурентной системе это будет намного важнее, чем пробелы. - person Aaron Bertrand; 19.07.2012

Атомарность гарантирует, согласно этой формулировке, что состояние базы данных остается неизменным. Вопрос в том, что мы подразумеваем под состоянием базы данных.

До тех пор, пока вы понимаете концепцию SQL «вставка идентификаторов», которая не утверждает и не гарантирует, что столбцы идентификаторов будут последовательными, проблем не возникает. Это требует переосмысления того, что гарантирует SQL, когда вы рассматриваете вставку идентификатора, но, поскольку мы знаем, что это может привести к сбою в упомянутом случае, на самом деле никогда не гарантировалось, что это значение NEXT.

Перед вставкой гарантируется, что «следующее» значение столбца идентификаторов будет только больше текущего значения, а не следующего значения. Это все еще состояние после.

person David Manheim    schedule 19.07.2012
comment
И даже бит больше, чем смывается, если вы активировали повторное заполнение. - person Aaron Bertrand; 19.07.2012