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つのグループにまとめて一度のネットワーク転送で複数のリクエストを完了させることをバッチ実行と呼びます。通常、「バッチ処理」とも呼ばれます。
なぜバッチ実行を使用する必要がありますか?
データの一貫性のためにバッチ実行を使用する場合もありますが、より多くの場合、バッチ実行を使用する最大の利点はパフォーマンスの向上にあります。これは以下の点で表れます:
- バッチステートメントはパフォーマンスを向上させるために再書き込みされます。
- バッチ実行はデータベースとのやり取り回数を減らすことができます。
- OceanBaseデータベースはバッチ実行を受信すると、いくつかの最適化処理を行い、さらにパフォーマンスを向上させることができます。
JDBCにおいて、どのクラス/オブジェクトを使用してバッチ実行を実現できますか?
Statementを使用する場合でもPrepareStatementを使用する場合でも、バッチ実行を実現できます。
バッチ実行を使用する場合、どのJDBC構成プロパティを設定する必要がありますか?
- 機能的には、バッチ実行を使用する場合は
rewriteBatchedStatements=TRUEを設定する必要があります。 - 実装と動作の観点から見ると、useServerPrepStmtsはバッチ実行の異なる動作を決定します。
- パフォーマンスの観点から見ると、cachePrepStmt、prepStmtCacheSize、prepStmtCacheSqlLimit、maxBatchTotalParamsNumはいずれもパフォーマンス向上に寄与します。
以下は構成プロパティの説明です:
| パラメータ | デフォルト値 | 説明 |
|---|---|---|
| allowMultiQueries | FALSE | 1つのステートメント内で「;」を使って複数のリクエストを分割できるかどうかを決定します。Batch実行はこのパラメータに依存せず、rewriteBatchedStatementsにのみ依存します。
説明 |
| rewriteBatchedStatements | FALSE | Batch実行中にINSERTステートメントを書き換えるかどうかを決定します。
|
| useServerPrepStmts | FALSE | Server側の準備済みステートメントを使用するかどうかを決定します。PrepareStatementオブジェクトにのみ有効です。
|
| cachePrepStmts | FALSE/TRUE | JDBCドライバーが準備済みステートメントをキャッシュするかどうかを決定します。Client側の準備済みステートメントとServer側の準備済みステートメントでは、キャッシュ内容が若干異なります。
説明 |
| prepStmtCacheSize | 25/250 | cachePrepStmtsが有効になっている場合、キャッシュできる準備済みステートメントの数を決定します。
説明 |
| prepStmtCacheSqlLimit | 256/2048 | cachePrepStmtsが有効になっている場合、キャッシュできる最大のSQLサイズを決定します。
説明 |
| maxBatchTotalParamsNum | 30000 | executeBatchを使用する場合、最大でいくつのパラメータを連結できるかを決定します。
説明 |
OceanBaseデータベースのどの構成パラメータがBatch実行に関係していますか?
以下の構成パラメータはBatch実行に関係しています:
| パラメータ | デフォルト値 | スコープ | 発効方法 | 意味 |
|---|---|---|---|---|
| ob_enable_batched_multi_statement | FALSE | テナント | 動的 | 一度に複数のステートメントをバッチ処理する機能を有効にするかどうかを設定します。このパラメータを有効にすると、Batch実行シナリオにおいて、Client/Serverがテキストプロトコルで通信する際、OceanBaseデータベースは形式が一致する複数のUPDATEステートメントを1つのステートメントとして解析し、対応するパラメータとデータの分布に基づいてBatch物理計画を生成します。 |
| _ob_enable_prepared_statement | FALSE | クラスタ | 動的 | Server側の準備済みステートメントを使用できるかどうかを示します。 |
| _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();PrepareStatementオブジェクトを使用する場合:
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();
以下の表は、PrepareStatementとStatementオブジェクトを使用した場合のBatch実行時の異なる動作を示しています(前提条件はrewriteBatchedStatements=TRUEです):
PrepareStatementオブジェクトを使用する場合:
| 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オブジェクトを使用します:
useServerPrepStmts INSERT TRUE 複数のINSERTステートメントのVALUESは、複数の「?」という形式で、1つのINSERTステートメントの複数のVALUESに連結されます。例:INSERT INTO TEST1 VALUES (?), (?),...,(?) シナリオ1では、OceanBaseサーバー側は1回のINSERTステートメントの
COM_STMT_PREPAREリクエスト(request_type=5)と1回のINSERTステートメントのCOM_STMT_EXECUTEリクエスト(request_type=6)を受信します。最適化の観点から見ると、以下の利点があります:- 2回の通信だけでINSERTステートメントのバッチ実行が完了します。
- PreparedStatementの本質的な特性により、コンパイル時間が短縮されます。
- その後にさらに多くのexecuteBatchが発生し、合理的なcachePrepStmtsおよび関連パラメータが設定されている場合、Prepareリクエスト(
request_type=5)の回数を減らし、executeリクエスト(request_type=6)のみを実行することができます。
シナリオ2
PreparedStatementオブジェクトを使用します:
useServerPrepStmts INSERT FALSE 複数のINSERTステートメントのVALUESは、複数の具体的な値として、1つのINSERTステートメントの複数のVALUESに連結されます。例:INSERT INTO TEST1 VALUES (1), (2),...,(10) シナリオ2では、OceanBaseサーバー側は1回のINSERTステートメントの
COM_QUERYリクエスト(request_type=2)を受信します。最適化の観点から見ると、以下の利点があります:- 1回の通信だけでINSERTステートメントのバッチ実行が完了します。
シナリオ3/4
Statementオブジェクトを使用します:
useServerPrepStmts INSERT シナリオ TRUE 複数の個別のINSERT文を「;」で連結する場合 シナリオ3 FALSE 複数の個別のINSERT文を「;」で連結する場合 シナリオ4 シナリオ3/4では、OceanBaseサーバー側は「;」で連結された複数のINSERTステートメントからなるリクエストを受信し、それらを順番に実行します。そのため、以下の利点もあります:
- 1回の通信だけでINSERTステートメントのバッチ実行が完了します。
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パッケージを選択して設定してください。
以下の表は、PrepareStatementおよびStatementオブジェクトを使用した場合のBatch実行時の異なる動作を示しています(前提条件はrewriteBatchedStatements=TRUE)。
PrepareStatementオブジェクトを使用する場合:
| 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オブジェクト:PrepareStatementオブジェクト
Server側パラメータ:
_ob_enable_prepared_statement=TRUEJDBC構成プロパティ:
rewriteBatchedStatements=TRUE useServerPrepStmts=TRUE cachePrepStmts=TRUE prepStmtCacheSize=<実際の状況に応じて> prepStmtCacheSqlLimit=<実際の状況に応じて> maxBatchTotalParamsNum=<実際の状況に応じて>
シナリオ2
JDBCオブジェクト:PrepareStatementオブジェクト
JDBC構成プロパティ:
rewriteBatchedStatements=TRUE useServerPrepStmts=FALSE
Batch UPDATE シナリオ2/3/4では、テキストプロトコルを使用して通信を行うため、複数のUPDATE文を一括処理する機能を利用できます。これらのシナリオでは、以下の構成が推奨されます:
シナリオ2
JDBCオブジェクト:PrepareStatementオブジェクト
Server側パラメータ:
ob_enable_batched_multi_statement=TRUE _enable_static_typing_engine=TRUEServer側変数:
_enable_dist_data_access_service=1JDBC構成プロパティ:
rewriteBatchedStatements=TRUE useServerPrepStmts=FALSE allowMultiQueries=TRUE --この設定は、JDBCドライバーの異なるバージョン間の動作の違いを回避するために設定されます
シナリオ3/4
JDBCオブジェクト:Statementオブジェクト
Server側パラメータ:
ob_enable_batched_multi_statement=TRUE _enable_static_typing_engine=TRUEServer側変数:
_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を識別する鍵は、論理読み取り行数、影響行数、および返される行数の変動を監視することです。具体的な識別ルールは以下の通りです:
- 論理読み取り行数の変動が1000行を超える。
- 影響行数の変動が1000行を超える。
- 返される行数の変動が10000行を超える。
ユーザーは、
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バインディングヒントの使用により、データベース管理者がSQL文に対する制御を追加する方法を参照できます。
SQL Outlineバインディングヒントの使用: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ヒント:大きなアカウントにこのヒントを追加することで、クエリが独立した実行計画を得られるようにします。
リソース消費の制限:「大きなアカウント」クエリがシステム性能に影響を与えるのを防ぐため、以下の方法でリソース消費を制限できます:
最大同時実行数の制御:
MAX_CONCURRENTヒントを使用します。例: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');OceanBase V4.3.3以降のバージョンでは、Hintを使用して特定のSQL文にリソースグループを指定できます:
select /*+ RESOURCE_GROUP('slow_group') */ * from items where store = 'taobao';大規模クエリタスクの自動分離:構成パラメータ
large_query_thresholdを使用して大規模クエリを判断し、CPU占有率を制限することで、小規模クエリが優先的に処理されるようにします。大規模クエリと小規模クエリが同時に存在する場合、大規模クエリはテナントワーカースレッドの最大30%しか占有できません。この30%という値は、構成パラメータlarge_query_worker_percentageで設定できます。
SQLチューニングに関するよくある質問
データベースの物理設計がクエリ性能を低下させる
クエリの性能は、アクセス対象オブジェクトのスキーマ情報などを含むデータベースの物理設計に大きく依存します。例えば、セカンダリインデックスについて、必要な投影列がインデックス列に含まれていない場合、メインテーブルへのアクセスには再テーブルアクセスの仕組みを使用する必要があり、クエリのコストは大幅に増加します。この場合、ユーザーの投影列をインデックス列に追加し、いわゆる「カバリングインデックス」を構築することで、再テーブルアクセスを回避できます。
システム負荷が単一SQLの応答時間に影響を与える
システム全体の負荷は、システム全体のスループットに影響を与えるだけでなく、単一のSQLの応答時間にも変化を引き起こします。OceanBaseデータベースのSQLエンジンはキュー方式を採用しており、ユーザーのリクエストに対して、利用可能なスレッドがすべて占有されている場合、新しいリクエストはリクエストキューに入れられ、特定のスレッドが現在のリクエストを完了するまで待機する必要があります。リクエストのキュー待ち時間は(G)V$OB_SQL_AUDITで確認できます。
コストモデルの欠陥による実行計画の誤選択
OceanBaseデータベースに組み込まれたコストモデルはサーバー固有のロジックであり、最適な実行計画はこのコストモデルに依存します。そのため、コストモデルに起因する計画選択の誤りが発生した場合、ユーザーは実行計画のバインディングを通じてのみ、「正しい」実行計画を選択することが保証されます。
クライアントルーティングとサーバー間でルーティングフィードバックロジックにエラーが発生する
obproxyの主要な機能の一つは、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つのノードに集中している場合。