1

私はMSSQLServer 2008を使用しています。常に使用されているテーブルがあります(データは常に変更されて挿入されます)。このテーブルには約7000万行が含まれています。このテーブルに対して、ストアドプロシージャを使用して簡単なクエリを実行しようとしています。適切に数日かかるはずです、

テーブルを引き続き使用できるようにする必要があります。ストアドプロシージャを実行しましたが、しばらくすると、テーブルで実行しようとするIDクエリによる単純な選択が応答しない/実行されないため、テーブルが壊れてしまいます。

私は何をすべきか?これが私のストアドプロシージャがどのように見えるかです:

 SET NOCOUNT ON;
update SOMETABLE
set
[some_col] = dbo.ufn_SomeFunction(CONVERT(NVARCHAR(500), another_column))
WHERE 
[some_col] = 243

where句でこれを試してみても('と'ロジックを使用して..):

ID_COL > 57000000 and ID_COL < 60000000 and

それでも動作しません

ところで-SomeFunctionはいくつかの簡単な数学アクションを実行し、約300kのアイテムを含む別のテーブルの行を検索しますが、変更されることはありません

4

1 に答える 1

3

私の見解では、サーバーには深刻なパフォーマンスの問題があります。クエリ内のレコードがないと仮定しても

select some_col with (nolock) where id_col between 57000000 and 57001000

メモリ内にあった場合、ディスクから数ページを順番に読み取るのに21秒かかることはありません(自動IDであり、「desc」を追加するなどの愚かなことをしなかった場合、id_colのクラスター化されたインデックスは断片化されるべきではありませんインデックス定義へ)。

しかし、それを修正できない/修正できない場合は、一度に100〜1000レコードなどの小さなパッケージで更新を行うことをお勧めします(ルックアップ関数が消費する時間によって異なります)。1回の更新/トランザクションは30秒以内で完了します。

トランザクションが完了するまで、更新のたびに変更したすべてのレコードが排他的にロックされていることがわかります。明示的なトランザクションを使用しない場合、各ステートメントは単一の自動トランザクションコンテキストで実行されるため、更新ステートメントが実行されるとロックが解放されます。

ただし、他のプロセスの動作によっては、その方法でデッドロックが発生する可能性があります。一度に複数のレコードを変更する場合、または複数の行で読み取りロックを収集して保持している場合でも、デッドロックが発生する可能性があります。

デッドロックを回避するには、updateステートメントが一度に変更するすべてのレコードをロックする必要があります。これを行う方法は、単一の更新ステートメント(id_colによって制限される数行のみ)を次のようなシリアル化可能なトランザクションに配置することです。

IF @@TRANCOUNT > 0
  -- Error: You are in a transaction context already

SET NOCOUNT ON
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE

-- Insert Loop here to work "x" through the id range
  BEGIN TRANSACTION
    UPDATE SOMETABLE
      SET [some_col] = dbo.ufn_SomeFunction(CONVERT(NVARCHAR(500), another_column))
      WHERE [some_col] = 243 AND id_col BETWEEN x AND x+500 -- or whatever keeps the update in the small timerange
  COMMIT
-- Next loop

-- Get all new records while you where running the loop. If these are too many you may have to paginate this also:
BEGIN TRANSACTION
  UPDATE SOMETABLE
    SET [some_col] = dbo.ufn_SomeFunction(CONVERT(NVARCHAR(500), another_column))
    WHERE [some_col] = 243 AND id_col >= x
COMMIT

更新ごとに、指定されたレコードに対して更新/排他的キー範囲ロックが適用されます(ただし、クラスター化されたインデックスキーを使用して更新を制限するため、これらのレコードのみがロックされます)。同じレコードの他の更新が完了するのを待ってから、ロックを取得し(他のすべてのトランザクションをブロックしますが、指定されたレコードのみをブロックします)、レコードを更新してロックを解除します。

最後の追加のステートメントは重要です。これは、「無限大」までのキー範囲のロックが必要であり、更新ステートメントの実行中に範囲の最後に挿入されることさえ防止するためです。

于 2013-03-18T16:54:29.673 に答える