0

SQL Serverにこのトリガーがあります

ALTER TRIGGER [dbo].[myTrigger]     
ON [dbo].[Data]     
AFTER INSERT 
AS 
BEGIN 

declare @number int

begin transaction
  select top 1 @number = NextNumber FROM settings

  Update Settings
    set NextNumber = NextNumber + 1

  UPDATE Data
    set number = @nnumber, currentDate = GetDate(), IdUser = user_id(current_user)
  FROM Data
    INNER JOIN inserted on inserted.IdData = Data.IdData

commit transaction

END

Data期待どおりに動作しますが、複数のユーザーが同時にテーブルに新しい行を追加した場合、期待どおりに動作するのだろうか?

4

2 に答える 2

2

このコードを少し分析してみましょう。

begin transaction

デフォルト設定を使用してトランザクションを開始しますREADCOMMITTED

select top 1 @number = NextNumber FROM settings

テーブルから最大の数を選択していSettingsます (ちなみに、必ず句を追加するORDER BY必要があります。そうしないと、順序が保証されません! ここで予期しない結果が生じる可能性があります)。

ただし、この操作はブロックされません - 2 つ以上のスレッドが同じ値、たとえば 100 を同時に読み取ることができます -SELECT非常に短い時間だけ共有ロックを取得し、共有ロックは互換性があります - 複数のリーダーが価値を同時に。

Update Settings
set NextNumber = NextNumber + 1

ここで、1 つのスレッドが青信号を受け取り、新しい値 (この例では 101) をテーブルに書き戻します。テーブルには排他的なロックがありUPDATEます (後で排他ロックにエスカレートされます) - 同時に書き込みできるスレッドは 1 つだけです

 UPDATE Data
 set number = @nnumber, currentDate = GetDate(), IdUser = user_id(current_user)
 FROM Data
 INNER JOIN inserted on inserted.IdData = Data.IdData

同じこと-1つの幸運なスレッドがDataテーブルを更新し、番号を設定し、更新中の100そのテーブルの行はトランザクションの終わりまでロックされます。

 commit transaction

これで、ラッキー スレッドがトランザクションをコミットして完了です。

ただし、同じ元の値 100 を読み取った 2 番目 (および場合によっては 3 番目、4 番目、5 番目 .....) のスレッドは、まだ「ループ内」にあります。スレッド #1 が完了したので、これらのスレッドの 2 番目のスレッド自分のことをするようになります - それはします。Settingsテーブルを新しい値 102 に正しく更新し、変数に読み込んだ「現在の」値を使用して、テーブルの 2 回目の更新を続けDataます....100@number

最終的に、テーブルから同じ元の値 (100) をすべて読み取る複数のスレッドが存在する可能性があり、それらのそれぞれがテーブルを同じ「新しい」値 (101)Settingsに更新します。Settings

ここで使用しているこの方法は、負荷がかかると安全ではありません

可能な解決策:

  1. 何よりもまず - これを行うための推奨される方法: テーブル内の列を使用して、データベースにこれ自体を処理させINT IDENTITYます (または、既に SQL Server 2012を使用している場合は、SEQUENCEオブジェクトを使用してすべての同期を処理します)。

  2. 何らかの理由でこれを行うことができない場合は、少なくとも、ビジーなシステムでもコードが機能することを確認してください! たとえば、最初のスレッドが来て現在の値を読み取るときに、テーブルにSELECT .... WITH (UPDLOCK)(排他的)UPDATEロックを設定するために使用する必要があります。これにより、最初のスレッドが完了するまで、他のすべてのスレッドが「現在の」値を読み取ることさえブロックされます。または、1 回の操作で古い値を更新して割り当てるなどの代替手段があります。SettignsUPDATE

于 2012-07-31T15:27:09.090 に答える
1

複数のユーザーによるストアド プロシージャの実行のシミュレート

SQL Server Management Studio で 2 つ (またはそれ以上) の編集ウィンドウを使用して、各ウィンドウでこのような操作を同時に実行できます。

insert into Data(ColName) values ('Value')
go 10000

go 10000バッチを 10000 回実行します。適切と思われる値に調整します。

于 2012-07-31T18:51:22.730 に答える