9

これは私の最初の質問でした:

SQL Server で EXCLUSIVE テーブル ロックを強制する方法を見つけようとしています。ISOLATION LEVEL を READ UNCOMMITTED に明示的に設定する非協力的なリーダー (私の制御の及ばない、クローズド ソースのもの) を回避する必要があります。その結果、挿入/更新中に指定したロックの数と分離の種類に関係なく、クライアントは適切な分離を設定するだけで、進行中のガベージの読み取りに戻ります。

答えは非常に単純であることが判明しました-

明示的なロックをトリガーする方法はありませんが、DDL を変更すると、探していたロックがトリガーされます。

この状況は理想的ではありませんが (反復可能な読み取りを監視する代わりにクライアントがブロックします)、クライアントに分離をオーバーライドさせてダーティ データを読み取らせるよりははるかに優れています。ダミー トリガー ロック メカニズムを使用した完全なコード例を次に示します。

勝った!

#!/usr/bin/env perl

Test::More を使用します。

警告を使用します。
厳密に使用します。

DBI を使用します。

my ($dsn, $user, $pass) = @ENV{ map { "DBICTEST_MSSQL_ODBC_$_" } qw/DSN USER PASS/ };

私の @coninf = ($dsn, $user, $pass, {
  自動コミット => 1、
  LongReadLen => 1048576、
  印刷エラー => 0、
  RaiseError => 1、
});

if (! フォーク) {
  私の $reader = DBI->connect(@coninf);
  $reader->do('SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED');

  "READER $$: テーブルの作成を待っています" と警告します。
  睡眠 1;

  (1..5) {
    is_deeply (
      $reader->selectall_arrayref ('SELECT COUNT(*) FROM artist'),
      [ [ 0 ] ],
      "READER $$: db に何も表示されません。しばらくスリープ状態です" . 時間、
    );
    睡眠 1;
  }

  出口;
}

私の $writer = DBI->connect(@coninf);

eval { $writer->do('DROP TABLE アーティスト') };
$writer->do('CREATE TABLE artist ( name VARCHAR(20) NOT NULL PRIMARY KEY )');
$writer->do(do('DISABLE TRIGGER _lock_artist ON artist');

睡眠 1;

is_deeply (
  $writer->selectall_arrayref ('SELECT COUNT(*) FROM アーティスト'),
  [ [ 0 ] ],
  '開始する行がありません',
);

$writer->begin_work;

$writer->prepare("INSERT INTO artist VALUES ('bupkus') ")->execute;
# これがロック方法です
$writer->do('ENABLE TRIGGER _lock_artist ON artist');
$writer->do('DISABLE TRIGGER _lock_artist ON artist');

is_deeply (
  $writer->selectall_arrayref ('SELECT COUNT(*) FROM アーティスト'),
  [ [ 1 ] ]、
  'ライターは挿入された行を見る',
);

# 遅延リーダー
睡眠 2;

$writer->ロールバック;

# リーダーに影響を与えるべきではありません
睡眠 2;

is_deeply (
  $writer->selectall_arrayref ('SELECT COUNT(*) FROM アーティスト'),
  [ [ 0 ] ],
  '何もコミットされていません (ライター)',
);

待つ;

done_testing;



結果:

READER 27311: mssql_isolation.t 行 27 でテーブルの作成を待機しています。
ok 1 - READER 27311: db に何も表示されず、1 秒間スリープします 1310555569
ok 1 - 開始する行がありません
ok 2 - Writer は挿入された行を認識します
わかりました 2 - リーダー 27311: db に何も表示されず、1 秒間スリープします 1310555571
ok 3 - READER 27311: db に何も表示されず、1 秒間スリープします 1310555572
ok 3 - 何もコミットされていません (ライター)
ok 4 - READER 27311: db に何も表示されず、1 秒間スリープします 1310555573
OK 5 - READER 27311: db に何も表示されず、1 秒間スリープします 1310555574
4

3 に答える 3

7

これを行うハック ハック ハック方法の 1 つは、テーブルで SCH-M ロックを取得する操作をテーブルで強制することです。これにより、READ UNCOMMITTED 分離レベルであっても、テーブルに対する読み取りが防止されます。たとえば、操作の一部として ALTER TABLE REBUILD のような操作を実行すると (おそらく、パフォーマンスへの影響を軽減するために特定の空のパーティションに対して)、コミットするまでテーブルへのすべての同時アクセスが防止されます。

于 2011-07-12T18:36:35.827 に答える
6

にロック ヒントを追加しますSELECT

SELECT COUNT(*) FROM artist WITH (TABLOCKX)

INSERTそしてあなたを取引に入れます。

最初のステートメントが明示的なトランザクション内にある場合、 はSELECT処理する前にロックを待機します。

于 2011-07-12T16:00:42.740 に答える
4

READ UNCOMMITTED接続が分離レベルにある場合、強制的にロックする直接的な方法はありません。

READCOMMITTED解決策は、テーブル ヒントを提供する、読み取られているテーブルのビューを作成することです。リーダーが使用するテーブル名を制御する場合、これは非常に簡単です。そうしないと、ライターを変更して新しいテーブルに書き込むかINSTEAD OF INSERT/UPDATE、ビューにトリガーを作成する必要があるため、かなり面倒です。

編集:

Michael Fredrickson は、テーブル ヒントを含むベース テーブルからの選択として単純に定義されたビューでは、トリガー定義を更新可能にする必要がないことを指摘しています。問題のある既存のテーブルの名前を変更し、それらをビューに置き換える場合、サードパーティのクライアントは賢明ではありません。

于 2011-07-12T17:17:54.070 に答える