【解決方法】Sqlbulkcopy はまったく同じ場所で失敗しますが、翌日には機能します


あるデータベースから別のサイトにテーブル全体をコピーできる機能があるプロジェクトに取り組んでいます。 各サイトにはそれぞれ約 30 のテーブルがあり、通常は同時に転送されますが、サーバーとネットワークを保護するために、同時にアクティブにできる「挿入」インスタンスは 7 つだけで、残りはスロットが空くのを待ちます。 これは、テスト段階で個々のサイトで問題なく機能し、毎秒 5000 行までの速度ですべてのテーブルをダウンロードできます。

その機能に加えて、クライアント サイト テーブルの変更を追跡し、別のテーブルに登録し、5 分ごとにサーバー サイトがそれらのデータを取得して、テーブルが新しい情報で更新されるようにする更新システムがあります。 これも問題なく動きます。

テスト中は 5 つのサイトしか使用しませんでしたが、完全には理解できない問題が発生したサイトをさらに追加し始めています。 2 つのサイトを追加し、それぞれ 30 回の挿入を問題なく行いましたが、3 回目のサイトの挿入中に、最大のテーブルが転送の途中で停止し、30 秒間待機してからエラーが表示されました。 いくつかの小さい 1 回は正常に完了しますが、最後の 1 回は同じ停止を経験し、30 秒待ってから失敗します。 次に、その 3 番目のサイトからのすべてのデータがサーバーから削除されるクリーンアップを実行してから、まったく同じ結果を確認するために再試行します。 最大のテーブルはまったく同じ場所で停止し、いくつかの小さなテーブルは完了すると停止しますが、最終的には転送の途中で停止します。

私たちのログでは、次のログ メッセージが複数回繰り返されていることがわかります。

15/12/2022 10:32:36.052	FAIL	TaskHandlerAddTabel[0]	Error during add table task Add [tablename] from [site] (ID: 2485008)
Exception type: AggregateException
One or more errors occurred. (Execution Timeout Expired.  The timeout period elapsed prior to completion of the operation or the server is not responding.
Operation cancelled by user.
The statement has been terminated.)
   at System.Threading.Tasks.Task.Wait(Int32 millisecondsTimeout, CancellationToken cancellationToken)
   at TaskHandlerAddTabel._DoWork(Object sender, DoWorkEventArgs e) in [path]\TaskHandlerAddTabel.cs:line 128
Exception type: SqlException
Execution Timeout Expired.  The timeout period elapsed prior to completion of the operation or the server is not responding.
Operation cancelled by user.
The statement has been terminated.
SQL error number: -2
   at Microsoft.Data.SqlClient.SqlConnection.OnError(SqlException exception, Boolean breakConnection, Action`1 wrapCloseInAction)
   at Microsoft.Data.SqlClient.TdsParser.ThrowExceptionAndWarning(TdsParserStateObject stateObj, Boolean callerHasConnectionLock, Boolean asyncClose)
   at Microsoft.Data.SqlClient.TdsParser.TryRun(RunBehavior runBehavior, SqlCommand cmdHandler, SqlDataReader dataStream, BulkCopySimpleResultSet bulkCopyHandler, TdsParserStateObject stateObj, Boolean& dataReady)
   at Microsoft.Data.SqlClient.TdsParser.Run(RunBehavior runBehavior, SqlCommand cmdHandler, SqlDataReader dataStream, BulkCopySimpleResultSet bulkCopyHandler, TdsParserStateObject stateObj)
   at Microsoft.Data.SqlClient.SqlBulkCopy.RunParser(BulkCopySimpleResultSet bulkCopyHandler)
   at Microsoft.Data.SqlClient.SqlBulkCopy.CopyBatchesAsyncContinuedOnSuccess(BulkCopySimpleResultSet internalResults, String updateBulkCommandText, CancellationToken cts, TaskCompletionSource`1 source)
Exception type: Win32Exception
The wait operation timed out.

このエラー ダンプは、カスタムおよびオリジナルの Sql メッセージとスタック トレース ダンプ (機密目的で一部編集されています) を組み合わせたものであり、転送には SqlBulkCopy を使用します。 分離された sqlbulk コピー コードを次に示します。

C#
SqlBulkCopy bulkCopy = globalDatalayer.CreateSqlBulkCopy(_server.GetConnectionStringWithUserPass());
bulkCopy.BatchSize = _bulkCopyLimit;
bulkCopy.EnableStreaming = true;
bulkCopy.DestinationTableName = $"tablename";
                
try
{
	var task = bulkCopy.WriteToServerAsync(dataReader, CancellationToken);
	task.Wait();
	CancellationToken.ThrowIfCancellationRequested();
}
catch
{
	throw;
}
finally
{
	try
	{
		bulkCopy.Dispose();
	}
	catch
	{
		//Ignored
	}
}

現在、SqlBulkCopy の実装は非常にうまく機能しているため、停止 -> 待機 -> エラー シーケンスが表示されるまでは、問題はないと思います。 また、追加された他のサイトで実行されている更新が正常に実行されており、変更が加えられていることもわかります。

私が試したこと:

私たちの現在の理論は、何らかの形で SQL Server を過負荷にしているというものです。その理由は次のとおりです。

説明したように、システムは最初の 2 つのサイトで機能しますが、その後、更新が正常に実行されている間に停止 -> 待機 -> エラーが発生し始めます。 しかし、システムを一晩放置して更新を実行し、朝に 3 番目のサイトを追加しようとすると、3 番目のサイトは成功します。 さらにサイトを追加し続けると、5 番目または 6 番目のサイトで stop->wait->error シナリオが発生します。 繰り返しますが、12~24時間放置すれば、翌日から再開できます。

サーバーのバイタルを調べましたが、すべて正常に見えます。 確かに、テーブルの追加中はサーバーがビジーですが、それほど重要ではありません。 何も叫んでいないようです。 問題なくサーバーに接続でき、サーバーを監視するために構築した監視クライアントは正常に動作しています。

したがって、サーバーへの接続は機能します。 テーブルの更新は、サイトごとに 5 分ごとに行われます (少量の作業)。 サーバー自体とバイタルへのアクセスは機能し、重要ではありませんが、いくつかのテーブルが挿入で攻撃された後 (さまざまなテーブルへの数千万の挿入を話します)、SqlBulkCopy は動作を停止しますが、しばらくの間 (12-24 時間)、その後、再び機能し始めます。 ここで何が起こっているかについて提案があるSQLサーバーの専門家。 私自身の理論では、何らかの方法でバッファーをいっぱいにするか、SQL サーバーにログインし、それがいっぱいになると、それが処理されるまでそれ以上処理できず、再び移動できるというものです。 12 ~ 24 時間のクールダウンはあまり調査されておらず、わずか 6 時間のウィンドウである可能性があります。 1時間待ってみましたが、役に立ちませんでした。

更新 1:

sqlbulkcopy のタイムアウトを延長しようとしましたが、タイムアウト例外が遅延するだけです。 更新を配信できない場合、クライアント側で実際に問題が発生する可能性があるため、永久に待機することは私たちが追求したいオプションではありません (挿入が最初に発生する必要があります)。 さらに、顧客は更新されたデータを取得できない理由を尋ね始めます。

また、サーバーの EventViewer を調査しましたが、SqlServer からは問題が報告されていません。

更新 2:

私の同僚の 1 人が今日もバッチを実行しましたが、結果は同じ失敗でした。 次に、別の同僚の提案として、いくつかのサーバープロセスを調べました。これは、クリーンアップルーチンが翌日に実行できる理由である可能性があります. とりわけ、インデックスを再構築し、いくつかのログを処理するルーチンがありました。 私たちはそれを実行し、再試行しました。現在、私たちが再実行したまぐれであると現在考えている 1 つを除いて、すべてのテーブルが完了し、機能しました。 そのため、テーブルの追加時にインデックスの自動再構築を行うことに取り組んでおり、それが問題の解決に役立つかどうかを確認しています. 私は今日、この問題を抱えていた別の人をオンラインで読みました。彼はインデックスを再構築していました。

解決策 1

エラーは非常に明確です。

One or more errors occurred. (Execution Timeout Expired.  The timeout period elapsed prior to completion of the operation or the server is not responding.

最初にすべきことは、Google に問題を報告することです。 SQL サーバー sqlbulkcopy タイムアウト – Google 検索[^]. 最初の結果は次のとおりです。 SqlBulkCopy.BulkCopyTimeout プロパティ (System.Data.SqlClient) | マイクロソフト ラーン[^]

ドキュメントには次のように記載されています。
BulkCopyTimeout プロパティの整数値。 デフォルトは 30 秒です。 値 0 は制限がないことを示します。 一括コピーは無期限に待機します。

また、タイムアウト期間を延長する方法も示します。

C#
// Set the timeout.
bulkCopy.BulkCopyTimeout = 60;

コメント

タイトルとURLをコピーしました