SqlTransaction завершено
У меня есть приложение, которое потенциально делает тысячи вставок в базу данных SQL Server 2005. Если сбой по какой-либо причине невозможен (ограничение внешнего ключа, длина поля и т.д.), Приложение предназначено для регистрации ошибки вставки и продолжения.
Каждая вставка не зависит от других, поэтому транзакции не нужны для целостности базы данных. Однако мы хотим использовать их для повышения производительности. Когда я использую транзакции, мы получим следующую ошибку примерно на 1 из каждых 100 коммитов.
This SqlTransaction has completed; it is no longer usable.
at System.Data.SqlClient.SqlTransaction.ZombieCheck()
at System.Data.SqlClient.SqlTransaction.Commit()
Чтобы попытаться отследить причину, я поместил инструкции трассировки при каждой операции транзакции, чтобы я мог убедиться, что транзакция не была закрыта перед вызовом commit. Я подтвердил, что мое приложение не закрывает транзакцию.
Затем я снова запустил приложение, используя те же самые входные данные, и он преуспел.
Если я выключу регистрацию, он снова сработает. Верните его, и он преуспеет. Это включение/выключение осуществляется через app.config без необходимости перекомпиляции.
Очевидно, что действие регистрации меняет время и заставляет его работать. Это указывает на проблему с потоками. Однако мое приложение не многопоточно.
Я видел одну запись MS KB, указывающую на ошибку с. framework 2.0 framework, которая может вызвать подобные проблемы (http://support.microsoft.com/kb/912732). Однако исправление, которое они предоставили, не решает эту проблему.
Ответы
Ответ 1
Трудно помочь, не видя кода. Я предполагаю, что из вашего описания вы используете транзакцию для фиксации после каждых N вставок, что улучшит производительность и фиксацию каждой вставки, если N не слишком большой.
Но недостатком является: если вставка не удалась, любые другие вставки в текущей партии N будут отброшены при откате транзакции.
В общем случае вы должны удалить транзакцию до закрытия соединения (которое откатит транзакцию, если она не была выполнена). Обычный шаблон выглядит примерно так:
using(SqlConnection connection = ...)
{
connection.Open();
using(SqlTransaction transaction = connection.BeginTransaction())
{
... do stuff ...
transaction.Commit(); // commit if all is successful
} // transaction.Dispose will be called here and will rollback if not committed
} // connection.Dispose called here
Пожалуйста, отправьте код, если вам нужна дополнительная помощь.
Ответ 2
Спасибо за отзывы. Я работал с кем-то из MSFT на форумах MSDN, чтобы выяснить, что происходит. Оказывается, проблема связана с тем, что одна из вставок не работает из-за проблемы с преобразованием даты.
Основная проблема заключается в том, что эта ошибка появляется, если это ошибка преобразования даты. Однако, если это еще одна ошибка, такая как слишком длинное поле, это не вызывает этой проблемы. В обоих случаях я ожидал, что транзакция все еще будет существовать, поэтому я могу называть ее откатом.
У меня есть полная программа для тиражирования этой проблемы. Если кто-то хочет его увидеть или обменять с помощью MSFT, вы можете найти поток в новостных группах MSFT в файле microsoft.public.dotnet.framework.adonet под потоком ошибок SqlTransaction.ZombieCheck.
Ответ 3
Имейте в виду, что ваше приложение не является единственным участником транзакции - SQL Server также задействован.
Ошибка, которую вы указываете:
Это SqlTransaction завершено; Это больше не используется. в System.Data.SqlClient.SqlTransaction.ZombieCheck() в System.Data.SqlClient.SqlTransaction.Commit()
не указывает, что транзакция завершена, только она завершена.
Мое первое предложение состоит в том, что ваш сервер отключил транзакцию, потому что он либо слишком долгое время (эллипсовое время на стене), либо слишком велико (слишком много изменений или слишком много блокировок).
Мое второе предложение - проверить, что вы правильно очищаете соединения и транзакции. Возможно, вы столкнулись с проблемами, потому что иногда вы изматываете пул некоторого ресурса, прежде чем вещи автоматически перерабатываются.
Например, DbConnection
реализует IDisposable, поэтому вам нужно убедиться, что вы правильно очистите - с помощью используя выражение, если вы можете, или, позвонив Dispose()
напрямую, если вы можете "т. 'DbCommand' аналогичен, поскольку он также реализует IDisposable
.
Ответ 4
Это исключение вызывается из-за того, что фактическая транзакция БД уже откатна, поэтому объект .NET, представляющий ее на стороне клиента, уже является "зомби".
Более подробное объяснение здесь. В этом сообщении объясняется, как написать правильный код отката транзакций для таких сценариев.
Ответ 5
Хорошо, сначала IMO, вы не должны ожидать, что ваше приложение будет иметь дело с такими "жесткими" ошибками. Ваша заявка должна понимать, что такое эти бизнес-правила, и учитывать их. НЕ принуждайте базу данных к бизнес-правилу или коду ограничения ограничений. Это должен быть только кодекс правил данных и при этом быть грациозным в отношении интерфейса с RETURN и другими методами. Компоновка схемы не должна сильно затягиваться.
Чтобы ответить на ваш вопрос, я подозреваю, не видя ни одного из вашего кода, что вы пытаетесь совершить фиксацию при возникновении ошибки, и вы еще этого не знаете. Это аргументы в пользу моего первого заявления... пытаясь уловить ошибку, которую дает база данных, без того, чтобы ваше приложение понимало и участвовало в этих правилах, вы боретесь с головной болью.
Попробуйте это. Вставьте строки, которые не сбой с ограничением базы данных, или другие ошибки, и обработайте вставки с фиксацией. Посмотрите, что произойдет. Затем запишитесь в некоторые записи, которые не пройдут, обработайте фиксацию и посмотрите, получите ли вы свою прекрасную ошибку. В-третьих, снова запустите ошибки, выполните принудительный откат и убедитесь, что вам удалось.
Просто некоторые идеи. Опять же, в качестве сводки, я думаю, что это связано с тем, что не изъятие определенных ошибок из базы данных изящным способом для вещей, которые являются "жесткими" ошибками, и ожидает, что перед ними будет работать с ними. Опять же, мое экспертное мнение, NOPE, этого не делают. Это беспорядок. Ваше приложение должно накладываться на знания о правилах на оборотной стороне. Эти вещи существуют только для того, чтобы убедиться, что это не происходит во время тестирования, и случайную ошибку, которая выглядит так, как показано на рисунке, чтобы затем поставить перед собой задачу обработать забытый поиск в таблице внешних ключей.
Надеюсь, что это поможет.
Ответ 6
Моя проблема была похожей, и оказалось, что я делал это сам с собой. Я запускал кучу скриптов в проекте VS2008 для создания хранимых процедур. В одном из процессов я использовал транзакцию. script выполнял создание proc внутри транзакции, а не включал код транзакции в качестве части процедуры. Поэтому, когда он дошел до конца без ошибок, он совершил транзакцию для script. Код .Net также использовал и затем совершил транзакцию, и эффект зомби пришел, когда он попытался закрыть транзакцию, которая уже была закрыта внутри SQL script. Я удалил транзакцию из SQL script, опираясь на транзакцию, открытую и проверенную в коде .Net, и это решило проблему.
Ответ 7
Согласно этому сообщению:
http://blogs.msdn.com/b/dataaccesstechnologies/archive/2010/08/24/zombie-check-on-transaction-error-this-sqltransaction-has-completed-it-is-no-longer-usable.aspx
Временное решение может состоять в том, чтобы попытаться поймать операцию отката или фиксации. Таким образом, этого кода будет достаточно, чтобы остановить выброс ошибки:
public static void TryRollback(this System.Data.IDbTransaction t)
{
try
{
t.Rollback();
}
catch (Exception ex)
{
// log error in my case
}
}
public static void TryCommit(this System.Data.IDbTransaction t)
{
try
{
t.Commit();
}
catch (Exception ex)
{
// log error in my case
}
}
Проверьте этот пример на веб-сайте msdn:
http://msdn.microsoft.com/en-us/library/system.data.sqlclient.sqltransaction.aspx
Ответ 8
Вы доказали, что ваши данные в порядке, вне всяких разумных сомнений.
FWIW,
Я бы предпочел переместить вставку в SPROC и вообще не использовать транзакцию.
Если вам нужно, чтобы пользовательский интерфейс был отзывчивым, используйте фоновый рабочий, чтобы выполнить хруст базы данных.
Для меня транзакция предназначена для взаимосвязанных действий, а не для экономии времени.
Стоимость вставки должна оплачиваться где-то вдоль линии.
Недавно я использовал профилировщик ANTS в приложении базы данных и был поражен, увидев прерывистые исключения SQlClient, демонстрирующие прочный код. Ошибки глубоки в структуре при открытии соединения. Они никогда не выходят на поверхность и не обнаруживаются кодом клиента.
Так...
Пункт?
Это не все твердые камни, перемещайте тяжелую работу с U.I. и принять стоимость.
НТН
Боб
Ответ 9
Я также столкнулся с той же проблемой.
This SqlTransaction has completed; it is no longer usable
После ряда исследований я обнаружил, что размер файла журнала базы данных составляет 40 ГБ.
Это большой объем данных, который вызывает транзакцию моей транзакции sql.
Итак, Мое решение - размер файла журнала shrink
, увидев эту ссылку ссылки.
CREATE PROCEDURE sp_ShrinkLog_ByDataBaseName(
@DataBaseName VARCHAR(200)
)
AS
BEGIN
DECLARE @DBName VARCHAR(200)
DECLARE @LogFileName VARCHAR(200)
SET @DBName = @DataBaseName
SELECT @LogFileName = [Logical File Name]
FROM
(
SELECT
DB_NAME(database_id) AS "Database Name",
type_desc AS "File Type",
name AS "Logical File Name",
physical_name AS "Physical File",
state_desc AS "State"
FROM
SYS.MASTER_FILES
WHERE
database_id = DB_ID(@DBName)
and type_desc = 'LOG'
) AS SystemInfo
SELECT LogFileName = @LogFileName
EXECUTE(
'USE ' + @DBName + ';
-- Truncate the log by changing the database recovery model to SIMPLE.
ALTER DATABASE ' + @DBName + '
SET RECOVERY SIMPLE;
-- Shrink the truncated log file to 1 MB.
DBCC SHRINKFILE (' + @LogFileName + ', 1);
-- Reset the database recovery model.
ALTER DATABASE ' + @DBName + '
SET RECOVERY FULL;
')
END
Затем выполните его EXEC sp_ShrinkLog_ByDataBaseName 'Yor_DB_NAME'
.
Если это не работает для вас, вот другое решение, которое, я думаю, будет работать.