プログラマー向けOracle DB

アプリケーションプログラマは、データベースの動作を理解する必要がありますか? オラクルのスペシャリストとして有名なTom Kiteは、著書 『Oracle for Professionals』で有名なasktomコラムの著者です。 アーキテクチャと主要な機能。これは必須であると主張します。 チームに有能な管理者がいる場合でも、Oracle DBMSがどのように機能するかを知ることは、そのような専門家がいない場合はもちろん、お互いをよりよく理解し、より効果的に対話するのに役立ちます。 このトピックでは、理解することでOracleデータベースを適切に操作し、その機能の一部を使用してアプリケーションに大きな利益をもたらすことができる主なものについて説明します。 Tom Kiteによる上記の本をすでに読んでいる場合は、この記事をメモとして使用できます。 1つの発言-この本を長い間読んだ後、Oracleデータベースの最新バージョンは9iでしたが、9つの管理コースも受講したため、トップ10で何かが変わって追加されたとしても、私を責めないでください。 私はかなり基本的なことについて書いていますが、それはあまり変わらないでしょう。

Oracleデータベースをこれほど高速にしているのはなぜですか?


データベース内のデータを変更すると、変更は最初にキャッシュに送られ、次にいくつかのスレッド(番号を構成可能)で非同期的にディスクに書き込まれます。 同時に、特別なログ(操作ログファイル)が書き込まれるため、キャッシュからディスクにフラッシュする時間がない場合は、障害後にデータを回復できます。 この方法では、すべてが1つのファイルでディスクに順番に書き込まれ、2つ以上のディスクに並行して書き込まれるように構成できるため、速度を上げることができます。これにより、変更の損失に対する保護の信頼性が向上します。 記述されたファイルがいくつかあり、それらは円で使用されます:ログファイルの1つで保護されているすべてのデータがバックグラウンドプロセスによってディスク上のデータブロックに書き込まれるとすぐに、このログファイルを再利用できます。 したがって、これにより、サークル内で使用される小さなログファイル専用の超高速の小さなディスクを使用することで、ある程度の節約が可能になります。

通常は、ディスク上のファイルに何かを保存するように求められたときにこれについて話します。すべてのデータを順番に書き込むため、ハードディスクのヘッドが走り回ってランダムブロックを探す必要がないため、「高速」になるためです。 私は、ここで何も得られないと主張します。遅いディスクに書き込みます。これは、他の多くのプロセスが大量の異なるログを書き込むために積極的に使用する可能性が高いためです。上記。

データ復旧メカニズム


Oracle DBMSで上記の操作ログファイルのアーカイブを有効にすると、すべての変更がアーカイブされます。 したがって、データブロックを含むディスクが失われた場合、現在のオンラインジャーナルを最新のアーカイブログファイルにロールすることで、クラッシュの直前を含め、いつでもそれらを復元できます。

コピースタンド


前述のアーカイブファイルをネットワーク経由で送信し、その場でデータベースのコピーに適用できます。 したがって、最小限のデータ遅延で常に手元にホットコピーがあります。 直前までデータを表示する必要がない一部のアプリケーションでは、そのようなデータベースを読み取り専用に構成し、メインデータベースインスタンスをアンロードできます。また、このようなインスタンスをいくつか読み取ることができます。

書き込み要求をハングさせる


リクエストの一部が任意の時点でフリーズする場合、不完全なチェックポイントについてalert.logを調べる価値があります。 これは、オンラインログファイルが大きすぎるか小さすぎるため、保護するデータがキャッシュからディスクにフラッシュされる時間がなく、DBMSが使用可能なすべてのオンラインログファイルを既にいっぱいにしており、それらを円で再び使用したいのですが、どうすればよいですか決して不可能ではなく、一時停止があります。 アプリケーションがJavaで実行されている場合、まずログのFull GCの存在を確認します。

ノンブロッキング読み取りおよびロールバックセグメント


Oracle DBMSの最も注目すべき機能の1つは、非ブロック読み取りです。これは、ロールバックセグメントによって実現されます。 データはほとんど常にロールバックセグメントから読み取ることができるため、Oracleへの読み取り要求がブロックされることはありません

ロールバックセグメントにはもう1つの利点があります。このセグメントから、特定の時点でテーブルに含まれていたテーブルの少し古いデータを読み取ろうとすることができます。 この機能はフラッシュバックと呼ばれます

ただし、ブタによってロールバックセグメントを配置できる場合があります。一括データ削除の大きな仕事がある場合(削除によりロールバックセグメントにすべてのデータが生成される)、 ORA-01555:snapshot too oldを取得できます。 この場合に覚えておくべき主なことは、N個の操作ごとにコミットするようにジョブを書き換える必要はありませんが、そのような操作には特別に作成された別のロールバックセグメントを使用する必要があるということです。

トランザクション分離レベル


Oracleには、READ_UNCOMMITED分離レベルはまったくありません。 実際には、他のデータベースでは、読み取りロックを削除することで最大の並列性を実現するために使用されます。 しかし、Oracleでは、読み取りは常にロックなしで実行されるため、追加の制限を導入することなく、このレベルで提供できるすべての利点を既に持っています。

一般に、Oracleでは2つの分離レベルのみが明確に使用可能です。デフォルトではREAD_COMMITTEDが使用されますが、必要に応じてSERIALIZABLEを設定できます。

ただし、ステートメントレベル(SELECT、UPDATEなど)では、デフォルトですでにREPEATABLE_READがあります。 1人のオペレーターのフレームワーク内で、常に一貫した読み取りを取得します。これは、もちろんロールバックセグメントにより達成されます。 Tom Kiteが提供する例は、これが何を提供するのかを説明するのにとても気に入りました。 アカウントを持つ非常に大きなテーブルがあり、SELECTを実行して金額を取得するとします。 Oracleでは、他の多くのデータベースとは異なり、クエリの途中で別のトランザクションが最初のアカウントから最後のアカウントに一定量を転送しても、最後の行に達するとSELECTに表示されるため、クエリの最初で実際のデータを取得できます行が変更された場合、ロールバックセグメントに移動し、クエリの開始時にこのセルにあったデータを読み取ります。 他の多くのデータベースでは、テーブルに決して存在しない量の形で応答を受け取ります。 ただし、この場合、OracleにはORA-01555:スナップショットが古すぎるという危険があります。

標準的な分離レベルに加えて、OracleにはREAD_ONLYトランザクションもあります。これは、単一のステートメント内だけでなく、トランザクション全体内でREPEATABLE_READを提供します。 しかし、名前が示すように、このようなトランザクションでは読み取りのみが可能です。

Oracleによるデータの効果的なキャッシュ


Oracleでは、すべてのデータはディスクに直接ではなく、キャッシュを介して読み書きされます。 デフォルトでは、キャッシュはLRUアルゴリズムに基づいているため、識別子によって非常に大きなプレートを大量に読み取り、そのたびに新しい行を要求すると、そのような要求は小さな静的タブレットをキャッシュから押し出す可能性があります。キャッシュ内。 このような目的のために、テーブルを作成するときに、テーブルへのクエリが送信される特別なタイプのキャッシュを指定できます。 したがって、上記の例の最初のテーブルにはRECYCLEキャッシュが適しています。これは基本的にデータを保存せず、キャッシュからすぐに破棄します。 また、2番目のテーブルにはKEEPキャッシュが適しています。これにより、小さな静的テーブルをキャッシュに保存でき、他のすべてのテーブルへのクエリはキャッシュから静的テーブルのデータを押し出しません。

空行


Oracleには非常に興味深い機能が1つありますが、それらは今では削除できません。 実際、データベースに空の行を入れると、NULLとして保存されます。 したがって、その後の読み取りでは、空の文字列は取得されず、NULLのみが取得されます。 同じ理由で、空の行はインデックスに分類されないため、実行計画でインデックスを使用するクエリを作成しても、空の(またはNULL)行は取得​​されないことに注意してください少し後で。

指数


Oracleには、Bツリー形式の既知のインデックスに加えて、いわゆるビットインデックスもあります。これは、非常にスパースな値を持つ列があるテーブルへのクエリで非常に高いパフォーマンスを示します。 この場合に特に効果的なのは、スパース列に対するORとANDの複雑な組み合わせがあるクエリ(通常のインデックスと比較して)を動作させることです。 このインデックスはBツリーではなくビットマップに格納されているため、記述されたリクエストを迅速に実行できます。 問題は、このインデックスがさらに望ましいテーブル内の一意の値の数です。非常に複雑です。10の一意の値または10,000のいずれかです。ここでは、特定のテーブルにインデックスを作成し、何が起こるかを確認する必要があります。 主なことは、インデックス付き列の挿入と更新が多数あるテーブルでこのインデックスを使用しようとしないことです。そのような操作は、インデックス付きテーブル内の非常に大きなセクションをブロックし、システムが危険を冒したり、デッドロックをキャッチすることさえあるためです。

Oracleで私が常に満足していたことの1つは、関数のインデックスを作成できることです。 つまり クエリで関数を使用する必要がある場合は、その関数にインデックスを作成して、読み取り操作を大幅に高速化できます。

インデックスのもう1つの興味深いプロパティは、インデックスがNULL値を格納しないことです。 したがって、インデックス付きの列で条件<、>または<>を使用してクエリを実行する場合、インデックス付きの列にNULL値を持つ行は返されません。 一方、このプロパティは特定の場合に非常に効果的に使用できます。 たとえば、注文が保存されている非常に大きなプレートがあり、これはクリーニングされません。 また、バックオフィスプロセスがあり、すべての注文をバックオフィスシステムに送信する必要があります。 最初の解決策は、is_sentフラグを使用して別の列を取得することです。このフラグは最初は0であり、送信時には1になります。つまり、 各開始時のバックグラウンドプロセスは、is_sent = 0の条件でテーブルをクエリします。 プレートは非常に積極的に補充されるため、ここではビットインデックスを使用できません。 Bツリーに基づく通常のインデックスは、膨大な数の行へのリンクを保存する必要があるため、多くのスペースを占有します。 ただし、送信マークとis_sent列の両方でロジックをわずかに変更すると、1の代わりにNULLが挿入されます。インデックスは非常に小さくなります。いつでもNULL以外の値のみを格納し、それらの数は非常に少ないためです。

テーブルは異なります


通常のテーブルに加えて、Oracleでは、他の多くのデータベースと同様に、これらのテーブルが主キーのインデックスツリーに直接存在する場合、いわゆるインデックステーブルがあります。 したがって、2つのことが一度に達成されます。まず、主キーのデータを読み取るために、読み取りが1つ少なくなります。次に、テーブル内のデータは主キーの順序で取得されるため、ORDER BY PK操作は追加の並べ替えなしで実行されます。 欠点には、このインデックスの操作ログファイルへのログインを区別できなくなるという事実が含まれます。

もう1つの優れたタイプのテーブルはクラスターテーブルです。これにより、1つのデータブロックの1つのキー値によってクラスター化された2つ以上のテーブルのデータを保存できます。 常にいくつかのテーブルを一緒に使用する場合、これは非常に効果的です。

クラスターテーブルに基づいて、Bツリーの代わりに、クラスターキーのハッシュに基づくテーブルがアクセスに使用されるクラスターハッシュテーブルもあります。 もちろん、非常に興味深いように聞こえますが、正直なところ、実際にはこれに遭遇したことがありません。

変数バインディング


おそらくすべてのプログラマーがこれについて既に聞いたことがあるでしょうが、それでも変数バインディングなどの必須のテクニックについて言及します。 実際には、一意の要求ごとに解析プランが作成され、キャッシュに入れられます。 IDによる非常に一般的なリクエストなど、さまざまなリクエストが多数ある場合、各リクエストに対してプランが生成され、さらに、他のすべてのプランがキャッシュから押し出され、データベースの応答時間が大幅に増加する可能性があります。

また、このケースでは多くの異なるクエリは存在しないので、is_deletedフラグなどの少数の異なる値を持つ列にそれを乱用してバインディングを使用するべきではないことに注意する価値があります。効果的な計画。

プログラマー向けの注意点


列のタイプがVARCHAR2(100)の場合、文字列longString.substring(0、100)を圧縮しようとすると、デフォルトの列定義の制限100は文字ではなくバイト数を参照するため、成功する事実はありません。 2バイト文字が存在する場合、問題が発生する可能性があります。 実際、この動作は少し設定できます 。詳細については、こちらを参照してください。 無限ループに挿入しようとしていない場合は、これまでのところそれを行うという原則に基づいて、この場合は「解決」することはありません。

さて、すべての種類のデータベースに対する一般的な推奨事項:1つのオブジェクトフィールドを変更するときに、テーブル内のすべての列を更新しないでください。 非常に明白なように見えますが、実践が示すように、このアンチパターンにはしばしば場所があるので、フレームワークが実際に変更されたフィールドのみを更新することを確認することを強くお勧めします。

おわりに


私の意見では、プログラマーにとって有用なもののほとんどを説明しようとしました。 それらの多くがあるので、私はそれらの概要を説明するだけで、多くの場合詳細を説明しません。 必要な設定を具体的に行う方法は、前述のTom Kiteの本で常に読むことができます。これはasktom列またはgoogleにあります。 主なことは、グーグルにすべきことを知ることであり、このトピックがあなたにこれを促したことを願っています。

Source: https://habr.com/ru/post/J120003/


All Articles