OceanBaseデータベースは、2種類の一貫性レベル(Consistency Level)を提供しています:STRONGとWEAKです。STRONGは強い一貫性を指し、最新のデータを読み取り、リクエストはプライマリレプリカにルーティングされます。WEAKは弱い一貫性を指し、最新のデータを読み取る必要がなく、リクエストはセカンダリレプリカに優先的にルーティングされます。OceanBaseデータベースの書き込み操作は常に強い一貫性であり、常にプライマリレプリカがサービスを提供します。読み取り操作はデフォルトで強い一貫性であり、プライマリレプリカがサービスを提供しますが、ユーザーは弱い一貫性の読み取りを指定することもでき、その場合はセカンダリレプリカが優先的にサービスを提供します。
一貫性レベルの指定方法
一貫性レベルを指定する方法は2つあります:
ob_read_consistencyシステム変数による指定セッション変数を設定し、現在のセッションに影響します。
obclient> SET ob_read_consistency = WEAK; obclient> SELECT * FROM t1; -- 弱い一貫性での読み取りグローバル変数を設定し、その後新規作成されるすべてのセッションに影響します。
obclient> SET GLOBAL ob_read_consistency = STRONG;
ヒント指定による指定
WEAK Consistencyを指定すると、
ob_read_consistencyよりも優先順位が高くなります。obclient> SELECT /*+READ_CONSISTENCY(WEAK) */ * FROM t1;STRONG Consistencyを指定します。
obclient> SELECT /*+READ_CONSISTENCY(STRONG) */ * FROM t1;
SQL文の一貫性レベル
書き込み文DML (INSERT/DELETE/UPDATE):強制的にSTRONG Consistencyを使用し、最新データに基づいた変更を要求します。
SELECT FOR UPDATE(SFU):書き込み文と同様に、強制的にSTRONG Consistencyを使用します。読み取り専用文
SELECT:ユーザーは異なるConsistency Levelを設定することで、さまざまな読み取り要件を満たせます。
トランザクションの一貫性レベル
弱い一貫性での読み取りのベストプラクティスは、トランザクション外のSELECT文にWEAK一貫性レベルを指定することです。これにより、その意味は明確になります。明示的にトランザクションを開始するシナリオでは、構文的にOceanBaseデータベースは異なる文に異なる一貫性レベルを設定することを許可しています。これにより、ユーザーは混乱する可能性があり、不適切に使用した場合はSQLエラーが発生する可能性があります。
原則:
- 一貫性レベルの最低設定は文レベルです。セッションまたは文が弱い読み取りとしてマークされている場合、システムは弱い読み取りパスを採用しようと試みます。
- 書き込み文とSFU文は強い読み取りのみを使用できます。弱い読み取りのマークが存在しても、これらの文は強い読み取り方式で実行されます。
- トランザクション内で、最初に書き込み文またはSFU文が実行された場合、その後の読み取り文がそのトランザクションの未コミット変更を読み取る必要がある場合は、強い読み取り方式で実行する必要があります。
以下に例を示します。
BEGIN;
-- 変更文、consistency_level=STRONG、トランザクション全体はSTRONGであるべきです
insert into t1 values (1);
-- SQL自体のconsistency_level=WEAKですが、最初のステートメントがSTRONGであるため、
-- このステートメントのconsistency_levelは強制的にSTRONGに設定されます。
select /*+READ_CONSISTENCY(WEAK) */ * from t1;
COMMIT;
BEGIN;
-- SFUは変更ステートメントであり、consistency_level=STRONGなので、トランザクション全体もSTRONGであるはずです。
select * from t1 for update;
-- SQL自体のconsistency_level=WEAKですが、最初のステートメントがSTRONGであるため、
-- このステートメントのconsistency_levelは強制的にSTRONGに設定されます。
select /*+READ_CONSISTENCY(WEAK) */ * from t1;
COMMIT;
BEGIN;
-- 最初のステートメントはWEAKです。
select /*+READ_CONSISTENCY(WEAK) */ * from t1;
-- セッションまたはグローバルのob_read_consistencyがWEAKに設定されていない場合、このステートメントはStrong Readモードで正常に実行されます。
select * from t1;
COMMIT;
BEGIN;
-- 最初のステートメントはWEAKです。
select /*+READ_CONSISTENCY(WEAK) */ * from t1;
-- 変更ステートメントは必ずSTRONGでなければならないため、このステートメントは正常に実行されます。
insert into t1 values (1);
-- SQL自体のconsistency_level=WEAKですが、前のステートメントが変更ステートメントであるため、
-- このステートメントのconsistency_levelは強制的にSTRONGに設定されます。
select /*+READ_CONSISTENCY(WEAK) */ * from t1;
COMMIT;
したがって、単一のSQLについて、一貫性レベルの決定ルールの優先順位は、大きい順に以下のように要約できます:
ステートメントタイプに基づいて決定される一貫性レベル。例えば、DMLやSFUは必ずSTRONGを採用します。
トランザクションの一貫性レベル。ステートメントがトランザクション内にあり、かつ最初のステートメントではない場合、トランザクションの一貫性レベルを採用します。
ヒントによって指定された一貫性レベル。
システム変数で指定された一貫性レベル。
デフォルトでSTRONGを採用します。
分離レベルとの関係
STRONG はすべての分離レベルをサポートします。
WEAK は読み取りコミット済み
READ COMMITTED分離レベルのみをサポートし、その他の分離レベルではOB_NOT_SUPPORTEDエラーが発生します。分離レベルが
Repeatable ReadおよびSerializableの場合でも、弱い一貫性のある読み取りリクエストが有効になります。
弱い一貫性のある読み取りのパラメータ
V2.2.x より前のバージョン
名称 |
レベル |
セマンティクス |
|---|---|---|
| enbale_causal_order_read | クラスタレベル | 単調読み取りを有効にするかどうか。デフォルトは false |
| max_stale_time_for_weak_consistency | クラスタレベル | 弱い一貫性のある読み取りの最大遅延時間。デフォルト値は 5 秒 |
V2.2.x より前のバージョンでは、Proxy レベルでのみ単調読み取りがサポートされています。つまり、クライアントが常に同じ Proxy にアクセスする限り、単調読み取りが保証されます。具体的な実装方法は、Proxy 上で単調増加する弱い一貫性のある読み取りのバージョン番号を維持することです。クライアントが Proxy をまたいでアクセスする場合、単調読み取りは保証されません。クラスタレベルでの単調読み取りが必要な場合は、V2.2.x 以降のバージョンを使用する必要があります。
V2.2.x ~ V3.x バージョン
名称 |
レベル |
セマンティクス |
|---|---|---|
| enable_monotonic_weak_read | テナントレベル | 単調読み取りを有効にするかどうか。デフォルトは True |
| max_stale_time_for_weak_consistency | テナントレベル | 弱い一貫性のある読み取りの最大遅延時間。デフォルト値は 5 秒 |
| weak_read_version_refresh_interval | クラスタレベル | 弱い一貫性のある読み取りバージョン番号のリフレッシュ間隔。デフォルト値は 50 ミリ秒 |
各パラメータの詳細な意味は以下の通りです:
enable_monotonic_weak_read:テナントレベルの単調読み取りスイッチ弱い一貫性のある読み取りは異なるレプリカにルーティングされ、異なるレプリカで読み取ったデータの新旧は保証されません。単調読み取りスイッチをオンにすると、OceanBase データベースは読み取ったデータバージョンがロールバックしないこと、つまり単調性が保証されることを保証します。典型的なユースケースは因果順序の保証です:2 つのトランザクション T1 と T2 があり、T1 がコミットした後に T2 がコミットします。クライアントが T2 トランザクションの変更を読み取った場合、その後必ず T1 トランザクションの変更を読み取ることができます。
max_stale_time_for_weak_consistency:弱い一貫性のある読み取りの最大遅延時間OceanBase データベースの弱い一貫性のある読み取りは有界古さの保証を提供します。つまり、読み取ったデータが
max_stale_time_for_weak_consistencyの時間だけ遅れることを保証します。デフォルト設定値は 5 秒で、テナントレベルでの設定をサポートしています。通常、各パーティションのスタンバイレプリカの遅延時間は 100 ミリ秒から 200 ミリ秒です。弱い一貫性のある読み取りの時効性は百ミリ秒レベルです。ネットワークジッター、リーダー不在などの状況が発生すると、弱い一貫性のある読み取りの時効性は低下します。一度にあるレプリカの遅延時間が
max_stale_time_for_weak_consistencyを超えると、そのレプリカは読み取り不可となり、内部の再試行メカニズムが他の有効なレプリカを再試行します。すべてのレプリカが読み取り不可の場合は、ステートメントがタイムアウトするまで継続的に再試行します。単調読み取りスイッチをオンにすると、OceanBase データベースは内部で各テナントに対してクラスタレベルの弱い一貫性のある読み取りバージョン番号を維持します。このバージョン番号も
max_stale_time_for_weak_consistencyの制約を満たします。その生成方法は、テナント内のすべてのパーティションレプリカの戻り進捗の最小値を統計することです。特定のパーティションレプリカの遅延時間がmax_stale_time_for_weak_consistencyを超えた場合は、そのレプリカは統計に含まれません。現在のメカニズムでは、1 つの遅延レプリカが全体の単調読み取りバージョン番号に影響します。例えば、2 つのパーティションの 2 つのレプリカがあり、1 つのレプリカが 100 ms、もう 1 つのレプリカが 1 秒遅れている場合、全体の単調読み取りバージョン番号は 1 秒となります。私たちは、レプリカが長時間遅れることは通常ではないと考えています。通常の状況では、単調読み取りバージョン番号は百ミリ秒レベルであるべきです。weak_read_version_refresh_interval:弱い一貫性のある読み取りバージョン番号のリフレッシュ間隔弱い一貫性のある読み取りバージョン番号のリフレッシュ間隔は、読み取ったデータの新旧の程度に影響します。設定値は
max_stale_time_for_weak_consistencyより大きくすることはできません。設定が 0 の場合、弱い一貫性のある単調読み取り機能は無効になり、クラスタレベルの弱い一貫性のある読み取りバージョン番号は維持されなくなります。また、これはクラスタレベルのパラメータであり、テナントレベルでの設定はサポートされません。
V4.x バージョン
4.x バージョンでは、max_stale_time_for_weak_consistency と weak_read_version_refresh_interval の 2 つのパラメータの機能は、以前のバージョンと一致しています。
弱い一貫性読み取りのタイムスタンプ
弱い一貫性読み取りは、通常、レプリカやスタンバイデータベースからのクエリ文を指します。OceanBaseデータベースの弱い一貫性読み取りは、依然としてトランザクションの一貫性ポイントを返し、未コミットのトランザクションや半分のトランザクションを返すことはありません。
弱い一貫性読み取りのタイムスタンプには、次の2つの側面が含まれます:
弱い読み取りを実行する前に、読み取りスナップショットデータを確定する必要があります。
パーティション自体が最大限に安全な読み取り可能なポイントをリアルタイムで維持しており、読み取りスナップショットがこのポイントより大きい読み取り要求は、そのレプリカを読むことができません。
タイムスタンプの生成方法
OceanBaseデータベースの弱い一貫性読み取りの一貫性は、大きく分けて2つのカテゴリーに分類されます:単調読み取りと非単調読み取りです。異なる機能によって、タイムスタンプの生成方法も異なります。
単調読み取り
単調読み取りとは、読み取り要求が発行された絶対時間に基づき、後に発行された要求が使用する読み取りスナップショットが前者より小さくないことを保証し、読み取ったデータがロールバックされないことを保証するものです。ここでの単調とは、ステートメントの読み取りスナップショットに関するものです。単調読み取りの保証は、クラスタレベルの単調弱い読み取りバージョン番号に依存しており、グローバルバージョン番号はロールバックされない必要があります。
グローバルバージョン番号の生成は、各OBServerノードが維持する最小最大安全バージョン番号に由来します。このバージョン番号の生成は、ローカルログ同期の進捗に依存します。OceanBaseデータベースでは、トランザクションのログデータは、apply service、replay_service、Transaction の3つのモジュールによって維持されます:
apply serviceは、トランザクションログのローカルディスクへの書き込み、スタンバイ機への送信、リーダーから同期されたログの受信などの操作を担当します。スタンバイ機にとって、それは各ログストリームに対してスライディングウィンドウを維持し、受信したログをlogidの小さい順に管理します。ログはスライディングウィンドウから順次滑り出し、replay_serviceに送られて再生されます。replay_serviceはログの再生を担当します。同じトランザクションの複数のログを同じワーカースレッドにハッシュし、同じトランザクションのログが順序良く再生されることを保証します。しかし、同じパーティションの複数のトランザクションは並行して再生されます。あるパーティションのログについて言えば、ログはコミットタイムスタンプに従って順序良くリンクリストに連結され、複数のスレッドによって並行して再生されます。Transactionはトランザクション状態の管理を担当します。Transactionはログの再生操作を実行します。つまり、RedoデータをMemStoreに再生し、トランザクションの状態を記録します。
上記の説明からわかるように、ログが apply service から受信され、replay_service に再生タスクとして渡され、さらに Transaction によってトランザクションログが再生されるまでには、時間がかかります。単一のパーティションのスタンバイ機が途中のトランザクションを読み取らないようにするためには、そのパーティションのスタンバイ機が読む安全なバージョン番号を見つけ、そのバージョン番号より前のすべてのトランザクションが既に再生完了していることを保証する必要があります。計算式は以下のとおりです:
slave_read_ts = min(replay_service_ts, apply_service_ts, trans_service_ts) - 1
ここで:
apply_service_tsはapply serviceのスライディングウィンドウ内で次に滑り出すか生成されるログのTimestampを表します。replay_service_tsは、そのログストリームでまだ再生されていないログのTimestampの最小値を表します。trans_service_tsは、現在再生中のすべてのトランザクションのprepare_log_tsの最小値を表します。
例を挙げて説明します:仮に apply service のスライディングウィンドウに2つのログが存在し、logidとログのコミットタイムスタンプがそれぞれ(10、100)、(11、200)であるとします。replay_service では、そのログストリームでまだ再生されていないログは (7、70)、(8、80) です。Transaction では、現在再生中のトランザクションは1つだけで、ちょうどredoとprepare logの再生が終わったところであり、prepare log のlogidは 9、prepare_log_ts=90です。この時点で、そのパーティションの 安全なバージョン番号=min(100, 70, 90) - 1 = 69 となります。
非単調読み取り
単調読み取りと比較して、非単調読み取りが保証する能力は弱いです。非単調読み取りは、前後に発行された要求のスナップショットが増加することを保証しません。同一データの異なるレプリカ間で同期進度に差異があるため、前後の2回の読み取りで異なるレプリカを読んだ場合、データがロールバックされる可能性があります。
原子性
単調読み取りであれ非単調読み取りであれ、V3.x およびそれ以前のバージョンでは、単一パーティション内での同一トランザクションのデータ変更の原子性を保証します。V4.x 以降のバージョンでは、単一ログストリーム内での同一トランザクションのデータ変更の原子性を保証します。
原子性の保証は以下の原則に従います:読み取りスナップショットが、単一パーティションまたはログストリームが維持する最大限に安全な読み取り可能なバージョン番号より小さい場合、読み取ったデータは必ず原子性を有します。