19

Oracle、SQL Server などのデータベースは、データの整合性が非常に優れています。データを保存するか失敗する (つまり ACID である) ことがわかっているデータ ストアを書きたい場合は、その下にある MySQL のようなデータベースを実際のストアとして使用します。これらの問題は既に解決されているからです。

しかし、コンピュータ科学の卒業生ではない私は、ACIDが実際に非常に低いレベルでどのように機能するのか疑問に思っています。たとえば、Oracleは常に「オンラインREDOログ」にデータを書き込み、アプリがトランザクションをコミットする必要があることを通知した時点で「コミット」を実行することを知っています。

ズームインして理解したいのは、この「コミット」段階です。「もう1バイト」をディスクに書き込むだけの場合ですか、それともa0をaに反転し1て、特定の行が正常に保存されたと言うのですか?

4

3 に答える 3

12

BerkeleyDB のドキュメントは、これらの実装がどのように機能するかを理解するのに実際に非常に役立つことを発見したことを覚えています。これは、リレーショナル/クエリ計画インフラストラクチャ全体なしでトランザクションを実装する非常に低レベルのデータベースであったためです。

すべてのデータベース (言及したものだけでも) がまったく同じように機能するわけではありません。PostgreSQL の最下位レベルの実装は、Oracle および SQL Server のスナップショット実装とはまったく異なりますが、それらはすべて同じアプローチ (MVCC: マルチバージョン同時実行制御) に基づいています。

ACID プロパティを実装する 1 つの方法は、ユーザー (ここでは「ユーザー」とは、変更を行うトランザクションです) がデータベースに対して行ったすべての変更を「トランザクション ログ」に書き込み、各行 (原子性の単位) を確実にロックすることです。コミットまたはロールバックするまで、他のトランザクションはそれを変更できません。トランザクションの最後に、コミットする場合は、コミットしたことを示すレコードをログに書き込み、ロックを解放します。. ロールバックする場合は、トランザクション ログをさかのぼってすべての変更を元に戻す必要があります。そのため、ログ ファイルに書き込まれた各変更には、データが最初にどのように表示されたかの「変更前のイメージ」が含まれます。(実際には、トランザクション ログもクラッシュ リカバリのために再生されるため、「アフター イメージ」も含まれます)。変更する各行をロックすることにより、トランザクションの終了後にロックを解除するまで、並行トランザクションは変更を認識しません。

MVCC は、更新によってブロックされるのではなく、行を読み取りたい同時トランザクションが代わりに「前のイメージ」にアクセスできる方法です。すべてのトランザクションには ID があり、どのトランザクションのデータを「見る」ことができ、どのトランザクションのデータを「見ることができない」かを決定する方法があります。このセットを生成するためのさまざまなルールを使用して、さまざまな分離レベルを実装します。したがって、「反復可能な読み取り」セマンティクスを取得するには、トランザクションは、たとえば、その後に開始されたトランザクションによって更新された行の「変更前イメージ」を見つける必要があります。これは単純にトランザクションにビフォア イメージのトランザクション ログを参照させることで実装できますが、実際にはそれらは別の場所に保存されます。したがって、Oracle には個別の REDO スペースと UNDO スペースがあり、REDO はトランザクション ログです。undo は、同時トランザクションが使用するブロックのビフォア イメージです。SQL Server は、変更前のイメージを tempdb に格納します。対照的に、PostgreSQL は行が更新されるたびに常に行の新しいコピーを作成するため、変更前のイメージはデータ ブロック自体に存在します。これにはいくつかの利点があります (コミットとロールバックはどちらも非常に単純な操作であり、管理する追加のスペースはありません)。 (これらの古い行バージョンは、バックグラウンドでバキュームする必要があります)。

PostgreSQL の場合 (これは私が内部構造に最も精通している DB です)、ディスク上の各行バージョンには、その行バージョンが「可視」であるかどうかを判断するためにトランザクションが調べなければならない追加のプロパティがあります。簡単にするために、"xmin" と "xmax" があるとします。"xmin" は行バージョンを作成したトランザクション ID を指定し、"xmax" はそれを削除した (オプションの) トランザクション ID を指定します (新しい行バージョンを作成して行の更新を表します)。したがって、txn#20 によって作成された行から開始します。

xmin xmax id value
20   -    1  FOO

そして txn#25 が実行されますupdate t set value = 'BAR' where id = 1

20   25   1  FOO
25   -    1  BAR

txn#25 が終了するまで、新しいトランザクションはその変更が表示されないと見なすことを認識します。したがって、このテーブルをスキャンするトランザクションは「FOO」バージョンを取得します。これは、その xmax が目に見えないトランザクションであるためです。

txn#25 がロールバックされた場合、新しいトランザクションはすぐにそれをスキップしませんが、txn#25 がコミットまたはロールバックされたかどうかを考慮します。(PostgreSQL は、これを提供するために「コミット ステータス」ルックアップ テーブルを管理します。pg_clog) txn#25 がロールバックされたため、その変更は表示されないため、再び「FOO」バージョンが取得されます。(そして、xmin トランザクションが表示されないため、"BAR" バージョンはスキップされます)

txn#25 がコミットされた場合、xmax トランザクションが表示される (つまり、そのトランザクションによって行われた変更が表示される) ため、"FOO" 行バージョンは取得されません。対照的に、xmin トランザクションが表示されている (xmax がない) ため、"BAR" 行バージョンが使用されます。

txn#25 がまだ進行中 (これも から読み取ることができます) の間、行を更新したい他のトランザクションは、トランザクション IDpg_clogで共有ロックを取得しようとすることによって、txn#25 が完了するまで待機します。私はこの点を強調しています。これが、PostgreSQL が通常「行ロック」自体を持たず、トランザクション ロックのみを持たない理由です。変更された各行に対するメモリ内ロックはありません。(使用するロックは、xmax を設定することによって行われ、xmax を示すフラグは削除ではなくロックを示すだけです)select ... for update

オラクル...多少似たようなことをしますが、詳細に関する私の知識ははるかに曖昧です。Oracle では、各トランザクションにシステム変更番号が発行され、それが各ブロックの先頭に記録されます。ブロックが変更されると、その元の内容が元に戻すスペースに置かれ、新しいブロックが古いブロックを指すようになります。したがって、基本的に、ブロック N のバージョンのリンクされたリスト (データ ファイル内の最新バージョン) があり、徐々に古いバージョンになります。 UNDO 表領域で。ブロックの上部には、ロックを何らかの方法で実装する「関心のあるトランザクション」のリストがあります (変更された行ごとにメモリ内ロックがありません)。それ以上の詳細は思い出せません。

私が信じている SQL Server のスナップショット分離メカニズムは、専用ファイルではなく、tempdb を使用して変更中のブロックを格納するという点で、Oracle のものとほぼ同じです。

このとりとめのない答えがお役に立てば幸いです。それはすべてメモリからのものであるため、特にpostgresql以外の実装では、大量の誤報が発生する可能性があります.

于 2012-02-09T14:06:04.453 に答える
2

Oracle の概要:

各 Oracle セッションは一意であり、各セッションは 1* アクティブなトランザクションを持つことができます。トランザクションが開始されると、Oracle は単調に増加するシステム変更番号 (SCN) をトランザクションに割り当てます。Oracle が行を更新/挿入/削除すると、Oracle は、書き込まれるブロックのヘッダーを更新し、「元の」ブロックを Oracle のロールバック (元に戻す) スペースに保存することによって、テーブルおよびサポート インデックス内の対象の行をロックします。また、Oracle は、メモリ バッファに REDO ログ エントリを書き込み、テーブル ブロックとインデックス ブロック、および UNDO ブロックの両方に加えられた変更を記述します。行われている変更は、ディスクに直接ではなく、メモリ内で行われていることに注意してください。

Oracle は、コミット時に、トランザクションの制御をクライアントに戻す前に、トランザクションの SCN まで (SCN を含む) のログ バッファ全体がディスクに書き込まれていることを確認します。

ロールバックする場合、Oracle はロールバック (元に戻す) の情報を使用して、加えられた変更を削除します。

では、これはどのように ACID を実装しますか。

アトミシティ: セッション、トランザクション、すべてが進行するか、またはまったく進行しないか。コミットすると、コミットが完了するまで何もできません。

一貫性: Oracle は、日付が日付であること、文字データが文字データであること、数値が有効であることをチェックします。チェック制約と同じこと。外部キー制約は、参照されている親キーが有効であり、処理中のトランザクションによって更新または削除されていないことを確認するためのチェックに依存しています。親キーが更新または削除された場合、ステートメントはハングします。実際には、親レコードに影響を与えるステートメントがコミットまたはロールバックするのを待っています。

インデペンデンス: それらのシステム変更番号を覚えていますか? 変更を加えていない場合、Oracle は、ステートメントを開始するかカーソルを宣言する時点で SCN が何であるかを認識しています。したがって、データが変更されている長期実行ステートメントがある場合、Oracle は、ステートメントの実行が開始されたときに AS IT WAS COMMITTED データを取得するためにチェックします。これは複数バージョンの一貫性管理であり、かなり複雑です。Oracle は、さまざまな SQL 標準で要求されるすべての分離レベルを実装しているわけではありません。たとえば、Oracle はダーティ リードやファントム リードを許可していません。

耐久性: ディスクにフラッシュされる REDO ログ バッファは、耐久性のルート レベルです。REDO ログ ファイルがいっぱいになると、Oracle はチェックポイントを強制します。このプロセスにより、Oracle は、コミットされているかどうかに関係なく、変更されたすべてのテーブル ブロックとインデックス ブロックをメモリからディスクに書き込みます。インスタンスがクラッシュし、データファイル内のデータにコミットされていない変更が含まれている場合、Oracle は REDO ログを使用してそれらの変更をロールバックします。これは、元に戻す情報が REDO ログにも含まれているためです。

*自律型トランザクションは複雑な問題であるため、当面は無視してください。

于 2012-02-09T17:46:12.633 に答える
0

Ayendeは Twitter で、彼がRavenDBRaven MQに使用している実際のデータ ストレージ メカニズムであるMuninを調べるように提案してくれました。

于 2012-02-09T15:05:47.873 に答える