SQL操作実行に関するFAQ
PL作成時のエラーを特定するにはどうすればよいですか?
SHOW ERRORSコマンドを使用して、ストアドプロシージャ作成時のエラー情報を確認できます。
PLのエラーログを分析するにはどうすればよいですか?
ユーザーが注目すべきは、クライアントに返されるエラーログのみです。これは、OceanBaseデータベースがResolve試行中に残されたエラー情報をログに記録するためです。また、PLにEXPECTION OTHERSステートメントが含まれている場合、その部分の実行時にエラー情報がログに残されます。したがって、PLのエラーログに記録される情報は不正確なものであり、無視できます。ユーザーが注目すべきは、クライアントに返されるエラーログのみです。
既に作成されたPLオブジェクトのソースコードを照会するにはどうすればよいですか?
DBA_SOURCE、ALL_SOURCE、またはUSER_SOURCEビューを照会することで、既に作成されたPLオブジェクトのソースコードを照会できます。ここで、TEXT列がPLオブジェクトのソースコードに該当します。
MySQLモードはINSERT ALL INTO構文をサポートしていますか?
サポートしていません。現在、INSERT ALL INTO構文はOracleモードのみがサポートしており、MySQLモードは一時的にサポートしていません。
SQLエンジンやトランザクションエンジンなど、テナントに対してリソースはどのように割り当てられ、分離されるのですか?
SQLエンジンとトランザクションエンジンはどちらもテナントごとに区別され、異なるテナント間では完全に隔離されています。具体的には:
- SQLエンジンのPlan Cacheとトランザクションエンジンのロックは完全に独立しています。
- CPU:テナントのSQLスレッドのCPU使用率を制御し、同時にアクティブなSQLスレッドを管理します。
- メモリ:異なるテナントのSQLメモリとトランザクションメモリは分離して管理されており、あるテナントのメモリが枯渇しても別のテナントに影響しません。
- スレッド:異なるテナントのSQLエンジンとトランザクションエンジンのスレッドは完全に独立しており、あるテナントのスレッドがスレッドセーブされても別のテナントに影響しません。
OceanBaseデータベースのバッチ実行とは何ですか?
JDBCを使用してOceanBaseデータベースとやり取りする際、複数のリクエストを1つのグループにまとめて、1回のネットワーク転送で複数のリクエストを完了させることをバッチ実行と呼び、一般的に「バッチ処理」とも呼ばれます。
なぜバッチ実行を使用するのですか?
データの一貫性のためにバッチ実行を使用する場合もありますが、より一般的には、パフォーマンス向上が最大の利点です。これは以下の点に表れています:
- バッチ文はパフォーマンス向上のために再構成されます。
- バッチ実行により、データベースとのやり取り回数が削減されます。
- OceanBaseデータベースはバッチ実行を受信すると最適化処理を行い、さらにパフォーマンスを向上させます。
JDBCにおいて、どのclass/objectがバッチ実行を実装できますか?
Statementを使用する場合でもPrepareStatementを使用する場合でも、バッチ実行を実装できます。
Batch実行を使用するには、どのJDBC構成プロパティを設定する必要がありますか?
- 機能的には、Batch実行を使用するには
rewriteBatchedStatements=TRUEを設定する必要があります。 - 実装と動作の観点から言えば、useServerPrepStmts は Batch実行の動作を決定します。
- パフォーマンスの観点から言えば、cachePrepStmt、prepStmtCacheSize、prepStmtCacheSqlLimit、maxBatchTotalParamsNum はすべてパフォーマンス向上に寄与します。
以下に関連する構成プロパティの説明を示します:
パラメータ |
デフォルト値 |
説明 |
|---|---|---|
| allowMultiQueries | FALSE | 1文内で「;」を使って複数のリクエストを分割できるかどうかを決定します。Batch実行はこのプロパティに依存せず、rewriteBatchedStatementsにのみ依存します。
説明 |
| rewriteBatchedStatements | FALSE | Batch実行中にINSERT文が書き換えられるかどうかを決定します。
|
| useServerPrepStmts | FALSE | サーバー側のプリペアドステートメントを使用するかどうかを決定します。PreparedStatementオブジェクトにのみ有効です。
|
| cachePrepStmts | FALSE/TRUE | JDBCドライバーがプリペアドステートメントをキャッシュするかどうかを決定します。クライアント側のプリペアドステートメントとサーバー側のプリペアドステートメントでは、キャッシュする内容が若干異なります。
説明 |
| prepStmtCacheSize | 25/250 | cachePrepStmtsを有効にした場合、キャッシュ可能なプリペアドステートメントの数を決定します。
説明 |
| prepStmtCacheSqlLimit | 256/2048 | cachePrepStmtsを有効にした場合、キャッシュ可能な最大SQLサイズを決定します。
説明 |
| maxBatchTotalParamsNum | 30000 | executeBatchを使用する場合、最大でいくつのパラメータを連結できるかを決定します。
説明 |
OceanBaseデータベースのどのパラメータがバッチ実行に関係していますか?
以下のパラメータがバッチ実行に関係しています:
パラメータ |
デフォルト値 |
順域 |
有効化方法 |
説明 |
|---|---|---|---|---|
| ob_enable_batched_multi_statement | FALSE | テナント | 動的 | 一括処理による複数文の実行を有効にするかどうかを設定します。このパラメータを有効にすると、Batch実行シナリオでClient/Serverがテキストプロトコルで通信する場合、OceanBaseデータベースは形式が一致する複数のUPDATE文を1つの文として解析し、対応するパラメータとデータ分布に基づいてBatch physical planを生成します。 |
| _ob_enable_prepared_statement | FALSE | クラスタ | 動的 | サーバー側のプリペアドステートメントを使用できるかどうかを示します。 |
| _enable_static_typing_engine | TRUE | クラスタ | 動的 | 新しいSQLエンジンを使用するかどうかを指定します。新旧のSQLエンジンは、Batch UPDATEを処理できるかどうかに違いがあります。古いエンジンはすべての主キーを含むBatch UPDATEのみを処理でき、新しいエンジンはすべての主キーを含まないBatch UPDATEも処理できます。 |
変数 |
デフォルト値 |
レベル |
意味 |
|---|---|---|---|
| _enable_dist_data_access_service | TRUE | SESSION/GLOBAL | SQLをDASモードで実行するかどうかを切り替えます。Batch UPDATEの最適化機能を利用するには、この変数を有効にする必要があります。 |
StatementとPrepareStatementの動作と使用方法の違いは何ですか?
Statementオブジェクトの使用:
conn = DriverManager.getConnection(obUrl); conn.setAutoCommit(false); Statement stmt = conn.createStatement(); String SQL = "INSERT INTO test1 (c1, c2) VALUES (1, 'test11')"; stmt.addBatch(SQL); String SQL = "INSERT INTO test1 (c1, c2) VALUES (2, 'test12')"; stmt.addBatch(SQL); String SQL = "INSERT INTO test1 (c1, c2) VALUES (3, 'test13')"; stmt.addBatch(SQL); int[] count = stmt.executeBatch(); stmt.clearBatch(); conn.commit();PreparedStatementオブジェクトの使用:
conn = DriverManager.getConnection(obUrl); conn.setAutoCommit(false); String SQL = "INSERT INTO TEST1 (C1, C2) VALUES (?, ?)"; PreparedStatemen pstmt = conn.prepareStatement(SQL); int rowCount = 5, batchCount = 10; for (int k=1; k<=batchCount; k++) { for (int i=1; i<=rowCount; i++) { pstmt.setInt(1, (k*100+i)); pstmt.setString(2, "test value"); pstmt.addBatch(); } int[] count = pstmt.executeBatch(); pstmt.clearBatch(); } conn.commit(); pstmt.close();
次の表は、PreparedStatementとStatementオブジェクトを使用した場合の、バッチ実行における動作の違いを示しています(前提条件:rewriteBatchedStatements=TRUE)。
PreparedStatementオブジェクトの使用:
useServerPrepStmts |
INSERT |
UPDATE |
シナリオ |
|---|---|---|---|
| TRUE | 複数のINSERTステートメントのVALUESは、複数の「?」を使用して、1つのINSERTステートメント内の複数のVALUESに連結されます。例:INSERT INTO TEST1 VALUES (?), (?),...,(?) | 複数の個別のUPDATEステートメントで、変数は「?」に置き換えられます | シナリオ1 |
| FALSE | 複数のINSERTステートメントのVALUESは、複数の具体的な値を使用して、1つのINSERTステートメント内の複数のVALUESに連結されます。例:INSERT INTO TEST1 VALUES (1), (2),...,(10) | 複数の個別のUPDATEステートメントが「;」で連結されます | シナリオ2 |
Statementオブジェクトの使用:
useServerPrepStmts |
INSERT |
UPDATE |
シナリオ |
|---|---|---|---|
| TRUE | 複数の個別INSERTステートメントを「;」で連結する場合 | 複数の個別UPDATEステートメントを「;」で連結する場合 | シナリオ3 |
| FALSE | 複数の個別INSERTステートメントを「;」で連結する場合 | 複数の個別UPDATEステートメントを「;」で連結する場合 | シナリオ4 |
OceanBaseデータベースのバッチ実行にはどのようなタイプがあり、それぞれのリクエストに対する最適化処理は何ですか?
ステートメントの観点から見ると、OceanBaseデータベースのバッチ実行は、INSERT、UPDATE、DELETEの処理が異なります。具体的には以下のとおりです:
説明
以下で説明するシナリオはすべて、rewriteBatchedStatements=TRUEを前提としています。
INSERT
シナリオ1
PreparedStatementオブジェクトを使用する場合:
useServerPrepStmtsINSERTTRUE 複数のINSERTステートメントのVALUESは、複数の「?」を連結した形で、1つのINSERTステートメントの複数のVALUESに結合されます。例:INSERT INTO TEST1 VALUES (?), (?),...,(?) シナリオ1では、OceanBaseサーバー側はINSERTステートメントの
COM_STMT_PREPAREリクエスト(request_type=5)とINSERTステートメントのCOM_STMT_EXECUTEリクエスト(request_type=6)をそれぞれ1回ずつ受信します。最適化の観点から見ると、以下の利点があります:- INSERTステートメントのバッチ実行を完了するために、通信は2回だけ発生します。
- PreparedStatementの本質的な特性により、コンパイル時間が短縮されます。
- 今後さらに多くのexecuteBatchが発生することを想定し、合理的なcachePrepStmtsおよび関連パラメータを設定することで、Prepareリクエスト(
request_type=5)の回数を減らし、executeリクエスト(request_type=6)のみを実行することができます。
シナリオ2
PreparedStatementオブジェクトを使用する場合:
useServerPrepStmtsINSERTFALSE 複数のINSERTステートメントのVALUESは、複数の具体的な値を連結した形で、1つのINSERTステートメントの複数のVALUESに結合されます。例:INSERT INTO TEST1 VALUES (1), (2),...,(10) シナリオ2では、OceanBaseサーバー側はINSERTステートメントの
COM_QUERYリクエスト(request_type=2)を1回受信します。最適化の観点から見ると、以下の利点があります:- INSERTステートメントのバッチ実行を完了するために、通信は1回だけ発生します。
シナリオ3/4
Statementオブジェクトを使用する場合:
useServerPrepStmtsINSERTシナリオTRUE 複数の個別INSERTステートメントを「;」で連結する場合 シナリオ3 FALSE 複数の個別INSERTステートメントを「;」で連結する場合 シナリオ4 シナリオ3/4では、OceanBaseサーバー側は複数のINSERTステートメントを「;」で連結した1つのリクエストを受信し、それらを順次実行します。そのため、以下の利点もあります:
- INSERTステートメントのバッチ実行を完了するために、通信は1回だけ発生します。
UPDATE
PreparedStatementオブジェクトを使用する場合:
useServerPrepStmts |
UPDATE |
シナリオ |
|---|---|---|
| TRUE | 複数の個別のUPDATEステートメントで、変数は「?」に置き換えられている場合 | シナリオ1 |
| FALSE | 複数の個別のUPDATEステートメントが「;」で連結されている場合 | シナリオ2 |
Statementオブジェクトを使用する場合:
useServerPrepStmts |
UPDATE |
シナリオ |
|---|---|---|
| TRUE | 複数の個別UPDATEステートメントを「;」で連結する場合 | シナリオ3 |
| FALSE | 複数の個別UPDATEステートメントを「;」で連結する場合 | シナリオ4 |
ob_enable_batched_multi_statementが有効でない場合、シナリオ1/2/3/4のUPDATEバッチ実行はOceanBase Server側で順次実行され、特別な最適化は行われません。ob_enable_batched_multi_statementが有効な場合、シナリオ2/3/4のUPDATEバッチ実行について、OceanBase Server側は形式が一致する複数のUPDATEステートメントを1つのステートメントとして解析し、対応するパラメータとデータ分布に基づいてバッチ物理計画を生成します。これにより、バッチUPDATE実行の効率が大幅に向上します。ただし、この機能を使用するには、明示的なトランザクションを有効にする必要があります。
DELETE 現在のバージョンでは、バッチDELETEステートメントに対する最適化効果はありません。
さまざまなシナリオで異なる設定を選択する方法は?
説明
可能な限り最新バージョンの oceanbase-client JAR パッケージを選択して設定してください。
次の表は、PreparedStatement と Statement オブジェクトを使用する場合の Batch 実行における動作の違いを示しています(前提条件は rewriteBatchedStatements=TRUE)。
PreparedStatement オブジェクトの使用:
useServerPrepStmts |
INSERT |
UPDATE |
シナリオ |
|---|---|---|---|
| TRUE | 複数のINSERTステートメントのVALUESは、複数の「?」を使用して、1つのINSERTステートメント内の複数のVALUESに連結されます。例:INSERT INTO TEST1 VALUES (?), (?),...,(?) | 複数の個別のUPDATEステートメントで、変数は「?」に置き換えられます | シナリオ1 |
| FALSE | 複数のINSERTステートメントのVALUESは、複数の具体的な値を使用して、1つのINSERTステートメント内の複数のVALUESに連結されます。例:INSERT INTO TEST1 VALUES (1), (2),...,(10) | 複数の個別のUPDATEステートメントが「;」で連結されます | シナリオ2 |
Statement オブジェクトの使用:
useServerPrepStmts |
INSERT |
UPDATE |
シナリオ |
|---|---|---|---|
| TRUE | 複数の個別INSERTステートメントを「;」で連結する場合 | 複数の個別UPDATEステートメントを「;」で連結する場合 | シナリオ3 |
| FALSE | 複数の個別INSERTステートメントを「;」で連結する場合 | 複数の個別UPDATEステートメントを「;」で連結する場合 | シナリオ4 |
Batch INSERT シナリオ1/2 は Batch 実行のパフォーマンスをより効果的に発揮できるため、推奨される設定です。具体的には、以下の設定を使用します:
シナリオ1
JDBC オブジェクト:PreparedStatement オブジェクト
サーバー側パラメータ:
_ob_enable_prepared_statement=TRUEJDBC 設定プロパティ:
rewriteBatchedStatements=TRUE useServerPrepStmts=TRUE cachePrepStmts=TRUE prepStmtCacheSize=<実際の状況に応じて> prepStmtCacheSqlLimit=<実際の状況に応じて> maxBatchTotalParamsNum=<実際の状況に応じて>
シナリオ2
JDBC オブジェクト:PreparedStatement オブジェクト
JDBC 設定プロパティ:
rewriteBatchedStatements=TRUE useServerPrepStmts=FALSE
Batch UPDATE シナリオ2/3/4 はテキストプロトコルを使用して通信を行うため、すべてマルチ UPDATE ステートメントのバッチ処理機能を利用できます。これらも推奨される設定であり、具体的には以下の設定を使用します:
シナリオ2
JDBC オブジェクト:PreparedStatement オブジェクト
サーバー側パラメータ:
ob_enable_batched_multi_statement=TRUE _enable_static_typing_engine=TRUEサーバー側変数:
_enable_dist_data_access_service=1JDBC 設定プロパティ:
rewriteBatchedStatements=TRUE useServerPrepStmts=FALSE allowMultiQueries=TRUE --この設定は、JDBC ドライバの異なるバージョン間での動作の違いを回避するために行います。
シナリオ3/4
JDBC オブジェクト:Statement オブジェクト
サーバー側パラメータ:
ob_enable_batched_multi_statement=TRUE _enable_static_typing_engine=TRUEサーバー側変数:
_enable_dist_data_access_service=1JDBC 設定プロパティ:
rewriteBatchedStatements=TRUE allowMultiQueries=TRUE --この設定は、JDBC ドライバの異なるバージョン間での動作の違いを回避するために行います。
OceanBaseデータベースのバッチ実行が有効かどうかを確認するにはどうすればよいですか?
最も一般的な方法は、gv$sql_audit を使用してバッチ実行が有効かどうかを確認することです。以下にいくつかのシナリオを例示します:
シナリオ1のBatch INSERTが有効な場合、
gv/sql_auditには次のレコードが表示されます:query_sql: insert into test_multi_queries (c1, c2) values (?, ?) request_type: 5 ps_stmt_id: 1 query_sql: insert into test_multi_queries (c1, c2) values (?, ?),(?, ?),(?, ?) request_type: 5 ps_stmt_id: 2 query_sql: insert into test_multi_queries (c1, c2) values (?, ?),(?, ?),(?, ?) request_type: 6 ps_stmt_id: 2シナリオ2のBatch INSERTが有効な場合、
gv/sql_auditには次のレコードが表示されます:query_sql: insert into test_multi_queries (c1, c2) values (1, 'PreparedStatement; rewriteBatchedStatements=true&allowMultiQueries=true&useLocalSessionState=true'),(2, 'PreparedStatement; rewriteBatchedStatements=true&allowMultiQueries=true&useLocalSessionState=true'),(3, 'PreparedStatement; rewriteBatchedStatements=true&allowMultiQueries=true&useLocalSessionState=true')シナリオ2のBatch UPDATEが有効な場合、
gv/sql_auditには次のレコードが表示されます:query_sql: update test2 set c2='batch update1' where c1=1;update test2 set c2='batch update2' where c1=2;update test2 set c2='batch update3' where c1=3 ret_code: 0 is_batched_multi_stmt: 1注意
ret_code = -5787の場合、Batch UPDATEは有効でないことを意味します。上記の説明に基づいて原因を調査する必要があります。
Batch実行時、executeBatchメソッドが返す値はいくらですか?
executeBatchメソッドが呼び出されると、整数配列int[]を返します。Batch INSERTおよびBatch UPDATEについては、以下のとおりです:
- OceanBaseクライアントで最終的に順次実行された場合、この配列はBatch内の各Operationによって変更された行数を返します。
- OceanBaseクライアントで最終的に一括で実行された場合、例えばJDBCドライバーが複数のINSERTステートメントを1つのINSERTステートメントの複数のvaluesに変更した場合(シナリオ1/2)、またはUPDATEステートメントがBatch physical planとして実行された場合(シナリオ2)、この配列の各要素は-2を返し、実行は成功したが更新行数が不明であることを示します。
SQLクエリの「サイズアカウント」問題の解決方法
「サイズアカウント」とは:
「サイズアカウント」問題とは、SQL文の解析および実行プロセスにおいて、同じSQL IDに対して異なる実行計画が対応するため、異なるクエリ条件で性能に顕著な差が生じる現象を指します。例えば、あるSQL文がパラメータ化された後、プランキャッシュにはすべてのケースに適さない実行計画が存在する可能性があります。これにより、以下の影響が発生します:
システムが特定のSQL(例:
select * from items where store = 'taobao';)を初めて実行する際、実行計画(p1)は選択率が極めて低い場合に適用される全表スキャンである可能性があります。その後、別のパラメータ化されたSQL(例:
select * from items where store = 'xiaomaibu';)が実行され、選択率が高い場合、元の計画p1が最適な解決策ではなくなり、性能が低下する可能性があります。「サイズアカウント」SQLの特定方法:
「サイズアカウント」SQLを特定する鍵は、論理読み取り行数、影響行数、戻り行数の変動を監視することです。具体的な識別ルールは以下の通りです:
- 論理読み取り行数の変動が1,000行を超える場合。
- 影響行数の変動が1,000行を超える場合。
- 戻り行数の変動が10,000行を超える場合。
ユーザーは、
v$ob_sql_auditなどのビューの実行レコードと統計情報を手動で集計することで、「サイズアカウント」SQLを特定できます。「サイズアカウント」SQL問題の解決方法:
プランキャッシュの無効化
単一SQLでplan cacheを無効にする:
USE_PLAN_CACHEヒントを使用します。例:SELECT /*+ USE_PLAN_CACHE(none) */ * FROM items WHERE store = 'taobao';または、セッション変数
ob_enable_plan_cacheを設定します:SET ob_enable_plan_cache = 0;
上記のキャッシュプランを無効にする方法は、アプリケーション内のSQL文を修正する必要があります。問題がアプリケーションの本番稼働後に発見された場合は、この問題を解決するためにアプリケーションの新しいバージョンをリリースする必要があります。SQL Outlineを使用したHintのバインディング方法を参照し、データベース管理者がSQL文に制御を追加することもできます。
SQL Outlineを使用したHintのバインディング:SQL outlineを利用し、DBAがSQLの実行計画を制御します。
CREATE OUTLINE otl_no_plan_cache1 ON select /*+ USE_PLAN_CACHE(NONE) */ * from items where store = 'taobao'; # またはSQL_IDを使用する;SQL_IDはV$OB_SQL_AUDITなどのビューから確認できる CREATE OUTLINE otl_idx_c2 ON 'ED570339F2C856BA96008A29EDF04C74' USING HINT /*+ USE_PLAN_CACHE(NONE) */;
SQL Plan Management (SPM)の有効化
OceanBase V4.2.1以降のバージョンでは、SPMを有効にすることを推奨します。このメカニズムは、特に複数の実行計画の中に受け入れられる単一の計画が存在する場合、「サイズアカウント」問題を緩和することができます。SPMの自動進化は、「Plan Cacheのクリア」時に計画が前向きに進化し、実行性能が向上することを保証します。
「サイズアカウント」SQLによる性能低下問題の解決
手動チューニング:適切な単一の実行計画がない場合、以下の方法で手動チューニングを行うことができます:
- インデックスの追加:インデックスを構築することでクエリ性能を向上させます。
- cursor_sharing_exact hint:大きなアカウントにこのhintを追加し、クエリが独立した実行計画を得られるようにします。
リソース消費の制限:「大きなアカウント」クエリがシステム性能に影響を与えるのを防ぐため、以下の方法でリソース消費を制限できます:
最大並列数の制御:
MAX_CONCURRENThintを使用します。例:CREATE OUTLINE otl_sql_throttle1 ON 'SQL_ID' USING HINT /*+ MAX_CONCURRENT(10) */;SQLレベルのリソース分離:大きなアカウントクエリをリソースグループにバインドし、実行中のリソース消費を制限します。例:
CALL DBMS_RESOURCE_MANAGER.SET_CONSUMER_GROUP_MAPPING( ATTRIBUTE => 'column', VALUE => 'items.store = \'taobao\'', CONSUMER_GROUP => 'slow_group');Hintを使用して、特定のSQL文にリソースグループを指定することができます:
select /*+ RESOURCE_GROUP('slow_group') */ * from items where store = 'taobao';大規模クエリタスクの自動分離:パラメータ
large_query_thresholdを設定して大規模クエリを判断し、そのCPU使用率を制限することで、小規模クエリが優先的に処理されるようにします。大規模クエリと小規模クエリが同時に存在する場合、大規模クエリはテナントワーカースレッドの最大30%を占有します。この30%という割合値は、パラメータlarge_query_worker_percentageで設定できます。
SQLチューニング FAQ
データベースの物理設計がクエリ性能を低下させる場合
クエリの性能は、アクセス対象のスキーマ情報などを含むデータベースの物理設計に大きく依存します。例えば、セカンダリインデックスにおいて、必要な投影列がインデックス列に含まれていない場合、メインテーブルへの再アクセス(リターントゥテーブル)が必要となり、クエリのコストが大幅に増加します。この場合、ユーザーの投影列をインデックス列に追加し、いわゆる「カバーインデックス」を構築することで、リターントゥテーブルを回避できます。
システム負荷が単一SQLの応答時間に影響を与える場合
システム全体の負荷は、システムの総合スループットに影響を与えるだけでなく、単一SQLの応答時間の変動も引き起こします。OceanBaseデータベースのSQLエンジンはキューモデルを採用しており、ユーザー要求に対して利用可能なスレッドがすべて占有されている場合、新しい要求は要求キューに並び、あるスレッドが現在の要求を完了するまで待機する必要があります。キュー内での要求の待ち時間は(G)V$OB_SQL_AUDITで確認できます。
コストモデルの欠陥による実行計画の誤選択
OceanBaseデータベースに組み込まれたコストモデルはサーバーの固有ロジックであり、最適な実行計画はこのコストモデルに依存します。したがって、コストモデルに起因する計画選択の誤りが発生した場合、ユーザーは実行計画のバインドを通じてのみ、「正しい」実行計画の選択を保証することができます。
クライアントルーティングとサーバー間でルーティングフィードバックロジックに誤りが発生した場合
obproxyの主要な機能の1つは、SQLクエリを適切なサーバーノードにルーティングすることです。具体的には、ユーザークエリが弱い整合性読み取り属性の使用を指定していない場合、Proxyは関連するテーブル(または特定のパーティション)のプライマリノードにルーティングする必要があります。これにより、サーバーノード間での2次転送を回避できます。そうでない場合、Proxyは事前に設定されたルールに基づいて適切なノードに転送します。 Proxyとサーバー間は疎結合方式を採用しているため、Proxy上でキャッシュされたデータの物理分散情報のリフレッシュが遅延する可能性があり、誤ったルート選択を引き起こすことがあります。ルーティング情報が変更される可能性のあるシナリオは以下のとおりです:
- ロードバランシングによるプライマリ再選出
- ネットワーク不安定によるサーバー間でのプライマリ再選出
- サーバーのオン/オフライン、ローテーションマージなどによるプライマリ再選出
SQL Auditや実行計画キャッシュで多数のリモート実行が検出された場合、上記のシナリオと一致するかどうかを検討する必要があります。クライアントとサーバー間にはルーティングフィードバックロジックがあり、誤りが発生するとクライアントは自動的にデータの物理分散情報をリフレッシュし、その後のルート選択も正常に戻ります。
インデックスモニタリング FAQ
インデックスモニタリングはデフォルトで有効になっていますが、パフォーマンスに影響しますか?
影響しますが、デフォルトの(SAMPLED)モードでは影響は非常に小さく、ほとんど無視できます。
サンプリングモードでのデータは正確ですか?例えば、1回クエリを実行した場合、必ず記録されますか?
必ずしもそうではありません。サンプリングの目的は一部のデータをフィルタリングすることなので、特定のクエリの実行履歴が欠落する可能性があります。
スタンバイデータベースのインデックスモニタリングは、自身のみを集計するのですか、それともプライマリデータベースと同期されますか?
集計はテナントレベルで行われます。スタンバイデータベースはプライマリデータベースから同期されたデータを参照できますが、書き込むことはできません。
パーティション交換に関するFAQ
OceanBaseデータベースのOracleモードでは、パーティションテーブルと非パーティションテーブルに同じグローバルインデックスが作成されていますが、なぜパーティション交換でエラーが発生し、インデックスが一致しないのですか?
OceanBaseデータベースのOracleモードでは、インデックスを作成する際にキーワードlocalを追加しない場合、デフォルトでグローバルストレージのインデックスが作成されます。パーティション交換を行うためには、パーティションテーブルのローカルストレージにあるインデックスと非パーティションテーブルのすべてのインデックスが一対一で一致している必要があります。一致しない場合は、パーティション交換を実行できません。
レプリケーションテーブル FAQ
レプリケーションテーブルとは?
レプリケーションテーブルは、OceanBaseデータベースがサポートする特殊なテーブルであり、任意のレプリカでデータの最新の変更を読み取ることができます。書き込み頻度が低く、読み取り操作の遅延やロードバランシングに対する要件が高い業務シナリオでの利用を推奨します。
レプリケーションテーブルの適用シナリオは?
適用シナリオ1:書き込み頻度が低く、読み取り操作の遅延とロードバランシングに対する要件が高いシナリオ
テーブルのデータ量がそれほど多くなく、アクセス頻度が特に高い場合、通常のテーブルを使用すると、データが1つのノードに集中し、ホットスポットが発生してパフォーマンスに影響を与えます。このような場合、レプリケーションテーブルを使用することを検討できます。
典型的なレプリケーションテーブルのシナリオ:
- 設定テーブル。業務ではこのテーブルから設定情報を読み取ります。
- 金融シナリオにおける為替レートを格納するテーブル。このテーブルはリアルタイムで更新されず、1日に1回のみ更新されます。
- 銀行支店または窓口情報テーブル。このテーブルに新規レコードが追加されることはほとんどありません。
レプリケーションテーブルのベストプラクティス:
- レプリケーションテーブルを作成する際は、必要に応じて選択することを推奨し、テナント内に多数のレプリケーションテーブルを作成することは避けてください。
- レプリケーションテーブルにデータを書き込む際、レプリケーションテーブルの書き込みと読み取りを同一トランザクション内で行うことは推奨されません。
- レプリケーションテーブルをクエリする際、
JOINクエリが含まれる場合は、通常のテーブルJOINレプリケーションテーブルの順序でクエリ(Query)SQLを設計してください。
適用シナリオ2:業務上パーティションテーブルに設定できないが、パーティションテーブルと頻繁にJOINするシナリオ
業務ロジック上の理由から、一部のテーブルはパーティションテーブルに分割できない、または分割する必要がない(例えば、クエリに明確なパーティション条件がない場合)が、頻繁にパーティションテーブルと関連付けられる場合があります。不要なクロスマシン分散クエリを削減するために、このようなテーブルをレプリケーションテーブルに設定し、各ノードで強力な読み取りをサポートすることができます。パーティションテーブルのパーティションのリーダーレプリカがどのマシンにあるかに関係なく、同じマシン上でレプリケーションテーブルのレプリカとテーブル接続を行うことができます。
レプリケーションテーブルの誤用シナリオは?
誤用シナリオ1:2つのパーティションテーブルのノード間接続を回避するために、そのうちの1つのパーティションテーブルをレプリケーションテーブルに設定する
パフォーマンス向上のために、無考慮にクエリ内のパーティションテーブルをレプリケーションテーブルに設定することは科学的ではありません。これらのテーブルが必ずしもレプリケーションテーブルの適用シナリオであるとは限らず、期待された効果が得られない可能性があります。
- テーブルに頻繁な書き込み操作が存在する場合、レプリケーションテーブルの書き込みにはパフォーマンスコストが伴います。すべてのレプリカに同期する必要があり、理論上は並行的に行われますが、個々のノードでネットワークなどの理由により同期が非常に遅くなると、レプリケーションテーブル全体の書き込みパフォーマンスが低下します。
- レプリケーションテーブルのデータ量が非常に大きい場合、レプリケーションテーブルは各ノードにレプリカを持つため、大量のストレージ容量を消費します。
誤用シナリオ2:トランザクション内にレプリケーションテーブルの書き込みと読み取りが混在する場合
レプリケーションテーブルに対する純粋な書き込みトランザクションまたは純粋な読み取りトランザクションは推奨されており、業務が書き込み遅延を許容できるかどうかに注意するだけで済みます。
トランザクション内にレプリケーションテーブルの書き込みと読み取りが混在する場合、リーダーを読み取る必要があるため、トランザクション内のすべてのレプリケーションテーブルが通常のテーブルのパフォーマンスに低下し、レプリケーションテーブルがローカルレプリカを読み取る利点を活用できなくなります。
誤用シナリオ3:レプリケーションテーブルと通常のテーブルのJOIN
ODP(obproxy)による複数テーブル JOIN のルーティングルールは、解決された最初のテーブルに基づいてルーティングすることです。レプリケーションテーブルの場合、ランダムに1つのレプリカを選択してルーティングします。もしそのノードが偶然にも通常のテーブルのリーダーでない場合、リモートルーティングが発生し、パフォーマンスに影響を与えます。
業務でレプリケーションテーブルと通常のパーティションテーブルの JOIN が関わる場合、JOIN の順序を通常のパーティションテーブル JOIN レプリケーションテーブルに調整し、通常のパーティションテーブルに基づいてルーティングし、予想通りローカル計画を生成してパフォーマンスを最適化することを推奨します。
誤用シナリオ4:レプリケーションテーブルをパーティションテーブルに設定する
レプリケーションテーブル自体、各ノードでレプリカが作成される仕組みになっているため、再度パーティションテーブルに設定する必要はありません。
自動インクリメント列 FAQ
どのような場合に自動インクリメント列のデータ型を BIGINT に設定する必要がありますか?
- 業務上、データの増加が速く、データの保持期間が長い場合。
- 業務上、テーブル作成時に
BIGINTを使用するかINTを使用するかは問題ではない場合。 - リーダーが分散し、マシンのリーダー切り替えの可能性が高まる場合(例:ダウン、ランダムロードバランシングなど)。これにより、自動インクリメント列の値が飛躍的に変化する可能性が高まります。
NOORDERモードで、自動インクリメント列の値を明示的に指定する必要がある場合。
どのような場合に自動インクリメント列のデータ型を INT に保つことが許容されますか?
- 業務上のデータ量が
INTの上限よりもはるかに小さい場合。 - MySQL から移行する業務で、
INT型を使用する必要がある場合。そうでない場合、アプリケーションに互換性の問題が発生する可能性があります。 - シングルマシン環境で、リーダー切り替えのシナリオがほとんどない場合。
- 監視・運用保守の能力があり、自動インクリメント値が上限に近づいた際に運用保守処理を行える場合。例えば、テーブルの再構築やデータのエクスポート、または
INTからBIGINTへの変更(V4.2.2 以降、列型をINTからBIGINTに変更する操作は Online DDL 操作となります)などです。
どのような場合に自動インクリメント列を NOORDER に変更できますか?
- ユーザーが自動インクリメント列のレベル順序を必要とせず、高同時実行操作のパフォーマンス最適化を望む場合、
ORDERをNOORDERに変更できます。 - ユーザーが自動インクリメント列のレベル順序を必要とするが、リーダーがすべて 1 つの OBServer に集中しており、高同時実行操作のパフォーマンス最適化を望む場合、
ORDERをNOORDERに変更できます。
どのような場合に自動インクリメント列の auto_increment_cache_size を小さくできますか?
- 値の飛躍的な変化が顕著な場合。
- 業務トラフィックが非常に低い場合。
- パフォーマンスに敏感でない場合。
- パフォーマンス要件はあるものの、シングルマシン環境でリーダーが 1 つのノードに集中している場合。