9

実行時間の長いクエリでタイムアウトの問題やデッドロックが発生することがあります。

NOLOCK をいつどこで使用するのが最も適切なのか疑問に思っています。

更新と挿入で使用しますか? または読み取りますか?

4

6 に答える 6

6

テーブルごとに nolock を指定できることに注意してください。

私は通常、複雑な SELECT クエリで nolock を使用しましたが、ほとんど変更されない小さなルックアップ テーブルと、表示のみのデータに対してのみ使用しました。現在の半年間の価格をリストした表、または ID から文字列への検索などを知っています。メジャー アップデートでのみ変更されるものであり、その後、サーバーは通常定期的に再起動されます。

これにより、パフォーマンスが大幅に向上し、最も忙しい時間にデッドロックが発生する可能性が減少しました。さらに重要なことに、多くのテーブルにアクセスするクエリの最悪のケースの瞬間に、それが本当に顕著になりました (これは論理的であり、ロックを取得する必要がなく、それらのサイドテーブル多くの場合、ロックが必要なテーブルは 7 ~ 8 個から 4 個に減少します)。

ただし、追加するときは十分に注意してください。急いだり、日常的に行ったりしないでください。正しく使用すれば害はありませんが、不適切に使用するとひどく損傷します。

非常に重要なもの、計算などには使用しないでください。一貫性がなくなり、遅かれ早かれ書き込みにつながるものは何でもなります。

このような別の最適化は、行レベルでのみロックする ROWLOCK です。これは主に、ログ レコードを挿入するだけのテーブルのように、行が互いに関連していないテーブルを更新 (または削除) する場合に役立ちます (そして、それらが挿入される順序は重要ではありません)。トランザクションの最後のどこかでログ レコードがテーブルに書き込まれるスキームがある場合、これもかなり高速化できます。

データベースの書き込みパーセンテージが比較的低い場合、それは価値がないかもしれません。読み取りと書き込みの比率は 2:1 未満でした。

これに取り組んでいるときに保存したいくつかの URL:

http://www.developerfusion.com/article/1688/sql-server-locks/4/

于 2009-05-03T09:43:45.090 に答える
5

SQL Server には 4 つのトランザクション分離レベルがあります。

  1. コミットされていない読み取り
  2. コミットされた読み取り
  3. 反復可能な読み取り
  4. シリアライズ可能

適用されるテーブルの場合、NOLOCK は「コミットされていない読み取り」と同等です。つまり、将来ロールバックされる可能性のあるトランザクションの行や、その他の多くの奇妙な結果を確認できます。

それでも、nolock は実際には非常にうまく機能します。特に、ビジネス レポートのように、わずかに間違ったデータを表示しても世界の終わりではない読み取り専用クエリの場合。特に請求書が関係する場合は、更新や挿入の近く、または一般的に意思決定コードの近くでは避けます。

nolock の代わりに、読み取りが多く、書き込みアクティビティが少ないデータベース向けの「コミット済みスナップショットの読み取り」を検討してください。次の方法でオンにできます。

ALTER DATABASE YourDb SET READ_COMMITTED_SNAPSHOT ON;

SQL Server 2005 以降で使用できます。これは Oracle がデフォルトでどのように動作するかであり、stackoverflow 自体が使用するものです。それについてのコーディング ホラーブログ エントリさえあります。

PS 実行時間の長いクエリとデッドロックは、SQL Server が間違った想定で動作していることを示している可能性もあります。統計またはインデックスが古くなっているかどうかを確認します。

SELECT 
    object_name = Object_Name(ind.object_id),
    IndexName = ind.name,
    StatisticsDate = STATS_DATE(ind.object_id, ind.index_id)
FROM SYS.INDEXES ind
order by STATS_DATE(ind.object_id, ind.index_id) desc

統計は、毎週の保守計画で更新する必要があります。

于 2009-05-03T10:13:45.907 に答える
3

最後の手段としてnolockを使用してください。ほとんどのデッドロックの問題は、クエリを調整したり、インデックスを調整したりすることで修正できます。過去5年間に、2つのうちの1つを調整しても修正できなかったデッドロックが1つ見られたと思います。

また、NOLOCKはselectステートメントでのみ尊重されることに注意してください。データの変更は常にロックされ、その動作は変更できません。したがって、ライター/ライターのデッドロック(非常に一般的)がある場合、ロックはまったく役に立ちません。

また、nolockは、ダーティデータを返すことに加えて、行が重複し(行が基になるテーブルから2回読み取られる)、行が欠落する(基になるテーブルの行がまったく読み取られなかった)可能性があることにも注意してください。

Nolockは、基本的にSQLServerに対して「結果が少し不正確であってもかまいません」という意味です。

スナップショットアイソレーションはオプションです。トランザクションの頻度と長さによっては、TempDBの負荷の増加が非常に深刻になる可能性があるため、最初に慎重にテストするようにしてください。また、スナップショットアイソレーションでデッドロックが発生することはありませんが、更新の競合が発生する可能性があることにも注意してください。繰り返しになりますが、アプリが正しく機能し、発生したエラーを処理できることをテストして確認してください。

于 2009-05-03T19:04:58.533 に答える
2

ダーティ リードやファントム レコードが許容される場合に使用します。たとえば、重要でないレポートを定期的に実行する場合、情報の正確性が主な要因ではなく、レコードの量やその他の指標を把握する必要がある場合に使用します。

于 2009-05-03T09:21:11.540 に答える
2

ダーティ データを読み取ってもよい場合は、nolock を使用する必要があります。データベースに多数の変更を加える可能性がある大規模なトランザクションは、まだ進行中の可能性があります。nolock を使用すると、それまでに設定されたデータが返されるだけです。そのトランザクションは、見ているデータをロールバックすると、間違っている可能性があります。したがって、返されるものが間違っている可能性があることが問題にならない場合にのみ使用してください。

デッドロックはよくある問題ですが、10 回中 9 回は完全に開発者の問題が原因です。nolock を使用するのではなく、デッドロックの原因を見つけることに専念します。多くの場合、1 つのトランザクションが他のすべてのトランザクションとは異なる順序で処理を行っています。その 1 つだけを修正すると、すべての問題が解消される可能性があります。

于 2009-05-03T09:48:50.757 に答える
1

読み取りロックを使用せずにトランザクション的に一貫性のあるビューを得るには、SQL Server でスナップショット分離を有効にすることをお勧めします。

これは、情報を読み取るときに、コミットされていないデータを表示する可能性ではなく、コミットされたデータのバージョンが結果に常に反映されるという点で、NOLOCK とは少し異なります。これにより、NOLOCK (「読み取り」ロックなし) と同じロックの同時実行性が提供され、より明確な結果が得られます。

トランザクションの一貫性があっても、表示を続けるデータや表示時に使用するデータは、いずれにせよ間違っているか、古い可能性があることに常に留意する必要があります。私はあまりにも多くの人が、データを十分に高速に使用したり、クエリ/トランザクション内でデータを使用したりすれば問題ないと思い込んでいるのを見てきました。これはばかげています。繰り返し可能な一貫性レベルは、悪い動作を助長するだけなので、最初から実装すべきではなかったというのが私の意見です。それらは Oracle には存在しません。

個人的には、特定の重要でないデータ ビューとレポートのロックを無効にするのが好きです。これにより、システムの負荷が軽減され、わずかに不正確な結果が得られる可能性が低くても問題にならないからです。

反復可能な読み取り一貫性レベルを利用し、ユーザー入力のために開いたトランザクションを保持するなどの罪を犯すことは、初期開発に関して開発者にとって少し楽になるかもしれませんが、ほとんどの場合、アプリケーションを合理的にスケーリングするという希望への主要な道路「ブロック」につながります。 .

私の見解では、更新をデータに適用するためには、常に条件を「再確認」するのが最善の方法です。

悪い:

UPDATE myaccount SET balance = 2000

より良い:

UPDATE myaccount SET balance = balance + 2000

さらに良い:

UPDATE myaccount SET balance = 2000 WHERE balance = 0 AND accountstatus = 1

最後に、アプリケーションは行数をチェックして、期待される行数が実際に更新されたことを確認してから、成功のフィードバックをユーザーに提示する必要があります。

于 2009-05-03T10:22:48.880 に答える