sql >> データベース >  >> RDS >> Database

ステートメントとトランザクションのACIDプロパティ

    [シリーズ全体のインデックスを参照]

    プログラマーなら誰でも、安全なマルチスレッドコードを書くのは難しいかもしれないと言うでしょう。細心の注意を払い、関連する技術的な問題を十分に理解する必要があります。データベース担当者として、T-SQLを作成する場合、この種の困難や複雑さは当てはまらないと思うかもしれません。したがって、T-SQLコードは、マルチスレッドプログラミングに最も一般的に関連する競合状態やその他のデータ整合性のリスクに対しても脆弱であることに気付くと、少しショックを受ける可能性があります。これは、単一のT-SQLステートメントについて話している場合でも、明示的なトランザクションで囲まれたステートメントのグループについて話している場合でも当てはまります。

    問題の核心は、データベースシステムが複数のトランザクションを同時に実行できるようにするという事実です。これはよく知られている(そして非常に望ましい)状態ですが、多くの本番T-SQLコードは、トランザクションまたはSELECTINSERTUPDATEDELETE 、またはMERGE

    コード作成者が同時データ変更の考えられる影響を認識している場合でも、明示的なトランザクションの使用は、実際に正当化されるよりも多くの保護を提供すると想定されることが多すぎます。これらの仮定や誤解は微妙である可能性があり、経験豊富なデータベースの専門家でさえ誤解を招く可能性があります。

    さて、これらの問題は実際的な意味ではそれほど重要ではない場合があります。たとえば、データベースが読み取り専用である場合や、その他の本物の保証がある場合があります。 私たちが作業している間、他の誰も基礎となるデータを変更しないこと。同様に、問題の操作は必要ではない場合があります 正確にある結果 正しい;データコンシューマーは、おおよその結果に完全に満足している可能性があります( any でデータベースのコミット状態を表していない場合でも) ある時点)。

    並行性の問題

    同時に実行されるタスク間の干渉の問題は、C#やJavaなどのプログラミング言語で作業するアプリケーション開発者にとっておなじみの問題です。解決策は多種多様ですが、通常は不可分操作を使用する必要があります または相互に排他的なリソース(ロックなど)を取得する )機密性の高い操作の進行中。適切な予防策が講じられていない場合、データの破損、エラー、または完全なクラッシュが発生する可能性があります。

    同じ概念(不可分操作やロックなど)の多くはデータベースの世界に存在しますが、残念ながら、それらには意味の重大な違いがあることがよくあります。 。ほとんどのデータベース担当者は、データベーストランザクションのACIDプロパティを認識しています。ここで、Aはアトミックを表します。 。 SQLServerもロックを使用します (および内部の他の相互排除デバイス)。これらの用語はどちらも、経験豊富なC#またはJavaプログラマーが合理的に期待することを意味するものではなく、多くのデータベースプロフェッショナルは、これらのトピックについても混乱して理解しています(お気に入りの検索エンジンを使用したクイック検索で証明されます)。

    繰り返しになりますが、これらの問題は実際的な問題ではない場合があります。データベースシステムでアクティブな注文の数をカウントするクエリを作成する場合、カウントが少しずれている場合、それはどの程度重要ですか?または、他の時点でのデータベースの状態を反映している場合はどうなりますか?

    実際のシステムでは、並行性と一貫性の間でトレードオフを行うのが一般的です(設計者がその時点でそれを意識していなかったとしても、情報に基づいて トレードオフはおそらくよりまれな動物です)。多くの場合、実際のシステムは十分に機能します 、異常が短命であるか、重要でないと見なされている場合。 Webページで一貫性のない状態が表示されるユーザーは、多くの場合、ページを更新することで問題を解決します。問題が報告された場合、再現性がないとしてクローズされる可能性があります。これが望ましい状況であると言っているのではなく、それが起こっていることを認識しているだけです。

    それでも、並行性の問題を基本的なレベルで理解することは非常に役立ちます。それらを知っていると、正しい(または情報に基づいた)書き込みが可能になります 状況に応じて、T-SQLを十分に修正します。さらに重要なことは、データの論理的な整合性を損なう可能性のあるT-SQLの記述を回避できることです。

    ただし、SQL ServerはACID保証を提供します!

    はい、そうですが、必ずしも期待どおりであるとは限らず、すべてを保護するわけではありません。多くの場合、人間は ACIDをはるかに多く読みます 正当化されるよりも。

    ACIDの頭字語で最もよく誤解されている要素は、Atomic、Consistent、Isolatedという単語です。これらについてはすぐに説明します。もう1つ、耐久性永続的にのみ適用されることを覚えている限り、十分に直感的です。 (回復可能)ユーザー データ。

    とはいえ、SQL Server 2014は、一般的な遅延耐久性とメモリ内のOLTPスキーマのみの耐久性の導入により、Durableプロパティの境界をいくらか曖昧にし始めています。完全を期すためにのみ言及します。これらの新機能についてはこれ以上説明しません。より問題のあるACIDプロパティに移りましょう:

    原子特性

    多くのプログラミング言語は不可分操作を提供します これは、競合状態やその他の望ましくない同時実行の影響から保護するために使用できます。この場合、実行の複数のスレッドが共有データ構造にアクセスまたは変更する可能性があります。アプリケーション開発者にとって、アトミック操作には完全な分離の明示的な保証が付属しています。 マルチスレッドプログラムでの他の並行処理の影響から。

    同様の状況は、複数のT-SQLクエリが異なるスレッドからの共有データ(つまりデータベース)に同時にアクセスして変更するデータベースの世界で発生します。ここでは並列クエリについて話しているのではないことに注意してください。通常のシングルスレッドクエリは、SQLServer内で別々のワーカースレッドで同時に実行されるように定期的にスケジュールされています。

    残念ながら、原子特性 SQLトランザクションの数は、トランザクション内で実行されたデータ変更がユニットとして成功または失敗することを保証するだけです。 。それ以上のものはありません。 完全な分離の保証は確かにありません 他の並行処理の影響から。また、アトミックトランザクションプロパティは読み取りに関する保証について何も述べていないことにも注意してください。 データ。

    単一のステートメント

    単一のステートメントについても特別なことは何もありません SQLServerで。明示的な包含トランザクション(BEGIN TRAN...COMMIT TRAN )が存在しない場合でも、自動コミットトランザクション内で単一のDMLステートメントが実行されます。同じACID保証が単一のステートメントに適用され、同じ制限も適用されます。特に、単一のステートメントには、進行中のデータが変更されないという特別な保証はありません。

    次のおもちゃのAdventureWorksクエリについて考えてみます。

    SELECT
        TH.TransactionID,
        TH.ProductID,
        TH.ReferenceOrderID,
        TH.ReferenceOrderLineID,
        TH.TransactionDate,
        TH.TransactionType,
        TH.Quantity,
        TH.ActualCost
    FROM Production.TransactionHistory AS TH
    WHERE TH.ReferenceOrderID =
    (
        SELECT TOP (1) 
            TH2.ReferenceOrderID
        FROM Production.TransactionHistory AS TH2
        WHERE TH2.TransactionType = N'P'
        ORDER BY 
            TH2.Quantity DESC,
            TH2.ReferenceOrderID ASC
    );

    クエリは、数量で最初にランク付けされた注文に関する情報を表示することを目的としています。実行計画は次のとおりです。

    この計画の主な操作は次のとおりです。

    1. テーブルをスキャンして、必要なトランザクションタイプの行を見つけます
    2. サブクエリの仕様に従って最も高くソートされる注文IDを見つけます
    3. 非クラスター化インデックスを使用して、選択した注文IDの行(同じテーブル内)を検索します
    4. クラスター化されたインデックスを使用して残りの列データを検索します

    ここで、同時ユーザーがOrder 495を変更し、そのトランザクションタイプをPからWに変更し、その変更をデータベースにコミットするとします。運が良ければ、この変更は、クエリが並べ替え操作を実行しているときに行われます(ステップ2)。

    ソートが完了すると、ステップ3のインデックスシークは選択された注文ID(495)の行を検索し、ステップ4のキールックアップはベーステーブル(トランザクションタイプは現在W)から残りの列をフェッチします。 。

    この一連のイベントは、クエリが明らかに不可能な結果を​​生成することを意味します:

    指定されたクエリとしてトランザクションタイプPの注文を検索する代わりに、結果にはトランザクションタイプWが表示されます。

    根本的な原因は明らかです。私たちのクエリは、単一ステートメントのクエリの進行中にデータを変更できないと暗黙的に想定していました。この場合の機会のウィンドウは、ソートのブロックのために比較的大きかったが、一般的に言えば、クエリ実行のどの段階でも同じ種類の競合状態が発生する可能性がある。当然のことながら、リスクは通常、同時変更のレベルが高くなり、テーブルが大きくなり、クエリプランでブロッキング演算子が表示される場所で高くなります。

    同じ一般的な領域でのもう1つの永続的な神話は、MERGEというものです。 個別のINSERTよりも優先されます 、UPDATE およびDELETE 単一ステートメントのMERGEのためのステートメント アトミックです。もちろん、それはナンセンスです。この種の推論については、シリーズの後半で説明します。

    この時点での一般的なメッセージは、明示的な手順を実行しない限り、データ行とインデックスエントリは、実行プロセス中いつでも変更、位置の移動、または完全に消失する可能性があるということです。データベース内の絶え間ないランダムな変化のイメージは、T-SQLクエリを作成するときに覚えておくとよいでしょう。

    一貫性プロパティ

    ACIDの頭字語の2番目の単語にも、さまざまな解釈があります。 SQL Serverデータベースでは、一貫性とはのみを意味します トランザクションがデータベースをアクティブな制約に違反しない状態のままにすること。そのステートメントがどれほど制限されているかを十分に理解することが重要です。データの整合性と論理的な一貫性を保証するACIDは、アクティブな制約によって提供されるものだけです。

    SQL Serverは、PRIMARY KEYなど、論理的な整合性を強制するための限られた範囲の制約を提供します 、FOREIGN KEYCHECKUNIQUE 、およびNOT NULL 。これらはすべて、トランザクションのコミット時に満たされることが保証されています。さらに、SQLServerは物理を保証します もちろん、データベースの整合性は常にあります。

    組み込みの制約は、必要なすべてのビジネスおよびデータ整合性ルールを適用するのに必ずしも十分ではありません。標準的な機能で創造性を発揮することは確かに可能ですが、これらはすぐに複雑になり、重複したデータが保存される可能性があります。

    結果として、ほとんどの実際のデータベースには、たとえばストアドプロシージャやトリガーなど、追加のルールを適用するために記述されたT-SQLルーチンが少なくともいくつか含まれています。このコードが正しく機能することを保証する責任は完全に作成者にあります。Consistencyプロパティは特定の保護を提供しません。

    この点を強調するために、T-SQLで記述された疑似制約は、どのような同時変更が発生しても、正しく実行される必要があります。アプリケーション開発者は、lockステートメントを使用してそのような機密性の高い操作を保護する場合があります。 T-SQLプログラマーがリスクのあるストアドプロシージャとトリガーコードのためにその機能に最も近いのは、比較的めったに使用されないsp_getapplockです。 システムストアドプロシージャ。それが唯一の、あるいは好ましい選択肢であると言っているのではなく、それが存在し、状況によっては正しい選択となる可能性があるということです。

    分離プロパティ

    これは、ACIDトランザクションのプロパティについて最もよく誤解されることがよくあります。

    原則として、完全に分離された トランザクションは、その存続期間中にデータベースに対して実行される唯一のタスクとして実行されます。他のトランザクションは、現在のトランザクションが完全に終了した(つまり、コミットまたはロールバックされた)場合にのみ開始できます。このように実行すると、トランザクションは本当に不可分操作になります。 、厳密な意味で、データベース以外の人がそのフレーズに帰するでしょう。

    実際には、データベーストランザクションは、代わりにある程度の分離で動作します。 現在有効なトランザクション分離レベルによって指定されます(スタンドアロンステートメントにも同様に適用されます。覚えておいてください)。この妥協点( 分離の)は、前述の並行性と正確性の間のトレードオフの実際的な結果です。文字通りトランザクションを1つずつ処理し、時間の重複がないシステムでは、完全な分離が提供されますが、システム全体のスループットは低下する可能性があります。

    次回

    このシリーズの次のパートでは、並行性の問題、ACIDプロパティ、およびトランザクション分離の調査を続け、シリアル化可能な分離レベルについて詳しく説明します。これは、考えていることを意味しない可能性のある別の例です。

    [シリーズ全体のインデックスを参照]


    1. HAProxyを使用してPostgreSQLレプリケーションセットアップ用の単一エンドポイントを作成する方法

    2. postgresqlのRownum

    3. MayBeSQLがMicrosoftAccessに登場!

    4. PrometheusとClusterControlを使用してProxySQLを監視する方法