OceanBaseデータベースは、2種類の一貫性レベル(Consistency Level)を提供しています:STRONGとWEAKです。STRONGは強い一貫性を指し、最新のデータを読み取り、リクエストはリーダーレプリカにルーティングされます。WEAKは弱い一貫性を指し、最新のデータを読み取ることを要求せず、リクエストは優先的にフォロワーレプリカにルーティングされます。OceanBaseデータベースの書き込み操作は常に強い一貫性であり、つまり常にリーダーレプリカがサービスを提供します。読み取り操作はデフォルトで強い一貫性であり、リーダーレプリカがサービスを提供しますが、ユーザーは弱い一貫性の読み取りを指定することもでき、その場合はフォロワーレプリカが優先的にサービスを提供します。
一貫性レベルの指定方法
一貫性レベルを指定するには、2つの方法があります。
ob_read_consistencyシステム変数を使用して指定するSession変数を設定し、現在のSessionに影響を与えます。
obclient> SET ob_read_consistency = WEAK; obclient> SELECT * FROM t1; -- 弱い一貫性の読み取りグローバル変数を設定し、その後に新規作成されるすべてのSessionに影響を与えます。
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;
-- Sessionまたはグローバルの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、シリアライズ可能Seriablizable、およびリピータブルリードRepeatable Readの分離レベルをサポートします。
弱い整合性読み取り構成パラメータ
V2.2.x以前のバージョン
| 名称 | 范囲 | 意味 |
|---|---|---|
| enable_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を超える場合は、そのレプリカは統計に含まれません。現在の仕組みでは、遅延しているレプリカが全体の単調読み取りバージョン番号に影響を与えます。例えば、2つのパーティションの2つのレプリカがあり、一方のレプリカが100ms遅延し、もう一方のレプリカが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 となります。
非単調読み取り
単調読み取りに比べて、非単調読み取りは保証される能力が弱いです。非単調読み取りは、前後に開始されるリクエストのスナップショットが増加することを保証しません。同一データの異なるレプリカ間で同期の進捗に差異があるため、前後で異なるレプリカを読み取ると、データがロールバックされる可能性があります。
原子性
単調読み取りであれ非単調読み取りであれ、単一パーティション内の同一トランザクションによって変更されたデータについては、原子性を保証できます。どのようなシナリオにおいても、読み取りスナップショットが単一パーティションが維持する最大の安全な読み取り可能なバージョン番号より小さい限り、読み取られるデータは必ず原子性を備えています。これが原子性保証の原則です。
したがって、設計上、パーティションが読み取り可能かどうかの検証を導入する必要があります。読み取り不可能なパーティションについては、SQL実行エンジンに他のレプリカを再試行させる必要があります。