85

READ_COMMITTED分離レベルがである Microsoft SQL Server 2005 データベースを使用していますREAD_COMMITTED_SNAPSHOT=ON

今私は使いたい:

SELECT * FROM <tablename> FOR UPDATE

...同じ行「FOR UPDATE」にアクセスしようとすると、他のデータベース接続がブロックされるようにします。

私は試した:

SELECT * FROM <tablename> WITH (updlock) WHERE id=1

...しかし、これにより、「1」以外の id を選択した場合でも、他のすべての接続がブロックされます。

SELECT FOR UPDATEOracle、DB2、MySql で知られていることを行うための正しいヒントはどれですか?

編集 2009-10-03:

テーブルとインデックスを作成するステートメントは次のとおりです。

CREATE TABLE example ( Id BIGINT NOT NULL, TransactionId BIGINT, 
    Terminal BIGINT, Status SMALLINT );
ALTER TABLE example ADD CONSTRAINT index108 PRIMARY KEY ( Id )
CREATE INDEX I108_FkTerminal ON example ( Terminal )
CREATE INDEX I108_Key ON example ( TransactionId )

多くの並列プロセスがこれを行いますSELECT:

SELECT * FROM example o WITH (updlock) WHERE o.TransactionId = ?

編集 2009-10-05:

より良い概要のために、次の表に試したすべての解決策を書き留めました。

メカニズム | 異なる行ブロックに対する SELECT | 同じ行ブロックに対する SELECT
-----------------------+-------------------------- ------+--------------------------
ローロック | いいえ | 番号
アップロック、ロウロック | はい | はい
xlock、rowlock | はい | はい
反復可能な読み取り | いいえ | 番号
DBCC トレーソン (1211,-1) | はい | はい
行ロック、xlock、ホールドロック | はい | はい
アップロック、ホールドロック | はい | はい
UPDLOCK、READPAST | いいえ | 番号

探しています | いいえ | はい
4

18 に答える 18

35

最近、Sql Server が必要以上に (ページ) ロックするため、デッドロックの問題が発生しました。あなたは本当にそれに対して何もできません。現在、デッドロック例外をキャッチしています...代わりにOracleがあればいいのにと思います。

編集:その間、スナップショット分離を使用しています。これにより、多くの問題が解決されますが、すべての問題は解決されません。残念ながら、スナップショット分離を使用できるようにするには、データベース サーバーによって許可されている必要があります。これにより、お客様のサイトで不要な問題が発生する可能性があります。現在、デッドロック例外 (もちろんまだ発生する可能性があります) だけでなく、バ​​ックグラウンド プロセスからトランザクションを繰り返す (ユーザーが繰り返すことはできない) スナップショットの同時実行性の問題もキャッチしています。しかし、これは以前よりもはるかに優れたパフォーマンスを発揮します。

于 2009-09-30T08:37:49.363 に答える
28

私は同様の問題を抱えています.1行だけをロックしたいです。私の知る限り、UPDLOCKオプションを使用すると、SQLSERVER は行を取得するために読み取る必要があるすべての行をロックします。そのため、行に直接アクセスするためのインデックスを定義しないと、先行するすべての行がロックされます。あなたの例では:

フィールドを持つ TBL という名前のテーブルがあるとしidます。で行をロックしたいid=10。フィールド ID (または選択した他のフィールド) のインデックスを定義する必要があります。

CREATE INDEX TBLINDEX ON TBL ( id )

次に、読み取った行のみをロックするクエリは次のとおりです。

SELECT * FROM TBL WITH (UPDLOCK, INDEX(TBLINDEX)) WHERE id=10.

INDEX(TBLINDEX) オプションを使用しない場合、SQLSERVER はテーブルの先頭からすべての行を読み取って で行を見つける必要がid=10あるため、それらの行はロックされます。

于 2012-03-22T08:05:44.507 に答える
9

スナップショット分離とブロッキング読み取りを同時に行うことはできません。スナップショット分離の目的は、読み取りのブロックを防ぐことです。

于 2009-09-30T08:46:38.043 に答える
6

おそらく mvcc を永続的にすることで解決できます (特定のバッチのみとは対照的に: SET TRANSACTION ISOLATION LEVEL SNAPSHOT):

ALTER DATABASE yourDbNameHere SET READ_COMMITTED_SNAPSHOT ON;

[編集: 10 月 14 日]

これを読んだ後: SQL Server よりも Oracle の方が同時実行性が優れていますか? そしてこれ:http://msdn.microsoft.com/en-us/library/ms175095.aspx

READ_COMMITTED_SNAPSHOT データベース オプションが ON に設定されている場合、オプションをサポートするために使用されるメカニズムがすぐにアクティブになります。READ_COMMITTED_SNAPSHOT オプションを設定すると、ALTER DATABASE コマンドを実行する接続のみがデータベースで許可されます。ALTER DATABASE が完了するまで、データベースに他の接続が開いていてはなりません。データベースは、シングル ユーザー モードである必要はありません。

特定のデータベースで mssql の MVCC を永続的にアクティブにするには、2 つのフラグを設定する必要があるという結論に達しました。

ALTER DATABASE yourDbNameHere SET ALLOW_SNAPSHOT_ISOLATION ON;
ALTER DATABASE yourDbNameHere SET READ_COMMITTED_SNAPSHOT ON;
于 2009-10-07T03:48:08.393 に答える
5

試す (updlock、rowlock)

于 2009-09-27T14:59:47.210 に答える
5

完全な答えは、DBMS の内部を掘り下げることができます。これは、クエリ エンジン (SQL オプティマイザーによって生成されたクエリ プランを実行するエンジン) の動作に依存します。

ただし、1 つの考えられる説明 (少なくとも一部の DBMS のバージョンに適用可能であり、必ずしも MS SQL Server に適用されるわけではありません) は、ID 列にインデックスがないため、' WHERE id = ?' を含むクエリを実行しようとするすべてのプロセスが終了することです。テーブルの順次スキャン、およびその順次スキャンは、プロセスが適用したロックにヒットします。DBMS がデフォルトでページ レベルのロックを適用する場合にも、問題が発生する可能性があります。1 つの行をロックすると、ページ全体とそのページのすべての行がロックされます。

これが問題の原因であることを暴く方法はいくつかあります。クエリ プランを見てください。インデックスを調べます。ID を 1 ではなく 1000000 にして SELECT を試し、他のプロセスがまだブロックされているかどうかを確認してください。

于 2009-09-27T15:00:42.260 に答える
3

行ロックを強制する偽の更新を作成します。

UPDATE <tablename> (ROWLOCK) SET <somecolumn> = <somecolumn> WHERE id=1

それがあなたの行をロックしていない場合、神はどうなるかを知っています.

この " UPDATE" の後、あなたSELECT (ROWLOCK)とその後の更新を行うことができます。

于 2011-06-14T03:27:12.103 に答える
3

OK、単一の選択はデフォルトで「Read Committed」トランザクション分離を使用します。これはロックし、そのセットへの書き込みを停止します。でトランザクション分離レベルを変更できます

Set Transaction Isolation Level { Read Uncommitted | Read Committed | Repeatable Read | Serializable }
Begin Tran
  Select ...
Commit Tran

これらについては、SQL Server BOL で詳しく説明されています

次の問題は、ロック トランザクションで ~2500 を超えるロックがある場合、または「通常の」メモリの 40% 以上を使用する場合、既定で SQL Server 2K5 がロックをエスカレートすることです。エスカレーションはページに移動し、次にテーブルロックに移動します

「トレース フラグ」 1211t を設定することで、このエスカレーションをオフにすることができます。詳細については、BOL を参照してください。

于 2009-10-03T21:32:15.513 に答える
2

アプリケーションロックは、「役立つ」ロックのエスカレーションを回避しながら、カスタムの粒度で独自のロックをロールする1つの方法です。sp_getapplockを参照してください。

于 2011-02-11T00:26:55.497 に答える
2

質問 - このケースはロックのエスカレーションの結果であることが証明されていますか (つまり、ロックのエスカレーション イベントをプロファイラーで追跡した場合、それがブロックの原因になっていることは間違いありません)? もしそうなら、完全な説明と、ロックのエスカレーションを防ぐためにインスタンス レベルでトレース フラグを有効にすることによる (かなり極端な) 回避策があります。http://support.microsoft.com/kb/323630トレース フラグ 1211を参照してください。

しかし、それは意図しない副作用を引き起こす可能性があります。

意図的に行をロックし、それを長期間ロックしたままにしておく場合、トランザクションに内部ロック メカニズムを使用するのは最善の方法ではありません (少なくとも SQL Server では)。SQL Server のすべての最適化は、短いトランザクション (入り、更新し、出て行く) を対象としています。それがそもそもロックエスカレーションの理由です。

そのため、長期間にわたって行を「チェックアウト」することが目的である場合は、トランザクション ロックの代わりに、値を持つ列と単純な古い更新ステートメントを使用して、行がロックされているかどうかを示すフラグを立てるのが最善です。

于 2010-02-19T00:01:54.327 に答える
2

この特定のクエリの実行中に他のセッションが行を読み取れるようにしたくないと仮定しています...

WITH (XLOCK,READPAST) ロック ヒントを使用しているときに SELECT をトランザクションでラップすると、必要な結果が得られます。他の同時読み取りが WITH (NOLOCK) を使用していないことを確認してください。READPAST を使用すると、他のセッションが同じ SELECT を別の行で実行できます。

BEGIN TRAN
  SELECT *
  FROM <tablename> WITH (XLOCK,READPAST) 
  WHERE RowId = @SomeId

  -- Do SOMETHING

  UPDATE <tablename>
  SET <column>=@somevalue
  WHERE RowId=@SomeId
COMMIT
于 2010-02-18T21:51:20.497 に答える
1

この記事によると、解決策はWITH(REPEATABLEREAD)ヒントを使用することです。

于 2009-09-30T09:00:37.413 に答える
1

コミット時に例外に対処し、トランザクションを繰り返す必要があります。

于 2009-11-08T21:10:01.430 に答える
1

すべてのクエリを再検討してください。SELECT FOR UPDATE がある同じテーブルから ROWLOCK/FOR UPDATE ヒントなしで選択するクエリがある可能性があります。


MSSQL は、これらの行ロックをページ レベルのロックにエスカレートすることがよくあります (クエリしているフィールドにインデックスがない場合は、テーブル レベルのロックでも)。この説明を参照してください。FOR UPDATE を要求するので、トランザクション レベル (財務、在庫など) の堅牢性が必要であると想定できます。したがって、そのサイトのアドバイスはあなたの問題には当てはまりません。MSSQLがロックをエスカレートする理由は単なる洞察です。


既に MSSQL 2005 (およびそれ以降) を使用している場合、それらは MVCC ベースです。ROWLOCK/UPDLOCK ヒントを使用した行レベルのロックに問題はないと思います。ただし、MSSQL 2005 以降を既に使用している場合は、FOR UPDATE する同じテーブルを照会するいくつかのクエリをチェックしてみてください。インデックスがある場合は、WHERE 句のフィールドをチェックしてロックをエスカレートさせます。


PS
私はPostgreSQLを使用しています.MVCCもFOR UPDATEを使用していますが、同じ問題は発生しません。ロックのエスカレーションは MVCC が解決するものなので、MSSQL 2005 がフィールドにインデックスを持たない WHERE 句を使用してテーブルのロックをエスカレートする場合は驚くでしょう。MSSQL 2005 でロックのエスカレーションが引き続き発生する場合は、WHERE 句のフィールドにインデックスがあるかどうかを確認してください。

免責事項: MSSQL を最後に使用したのはバージョン 2000 のみです。

于 2009-09-30T09:37:31.300 に答える
1

行ロックの問題をまったく別の方法で解決しました。私は、SQL Server がそのようなロックを満足のいく方法で管理できないことに気付きました。ミューテックスを使用してプログラムの観点からこれを解決することにしました... waitForLock... releaseLock...

于 2011-06-04T20:48:26.513 に答える
1

使用してみてください:

SELECT * FROM <tablename> WITH ROWLOCK XLOCK HOLDLOCK

これにより、ロックが排他的になり、トランザクションの間保持されます。

于 2009-09-27T15:38:52.497 に答える
0

最初に(実際にデータを変更せずに)この行を簡単に更新してみてはどうでしょうか。その後、更新用に選択されたような行に進むことができます。

UPDATE dbo.Customer SET FieldForLock = FieldForLock WHERE CustomerID = @CustomerID
/* do whatever you want */

編集:もちろんトランザクションでラップする必要があります

編集2:別の解決策はSERIALIZABLE分離レベルを使用することです

于 2011-10-23T17:36:20.207 に答える
0

READPASTを試しましたか?

テーブルをキューのように扱うときは、UPDLOCK と READPAST を一緒に使用しました。

于 2009-10-02T21:59:18.993 に答える