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を使用する場合でも、バッチ実行を実現できます。
バッチ実行を使用するには、どのようなJDBC設定プロパティを設定する必要がありますか?
- 機能的には、バッチ実行を使用するには
rewriteBatchedStatements=TRUEを設定する必要があります。 - 実装と動作の観点から言えば、useServerPrepStmtsはバッチ実行の異なる動作を決定します。
- パフォーマンスの観点から言えば、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データベースのどの構成パラメータがBatch実行に関係していますか?
以下の構成パラメータがBatch実行に関係しています:
パラメータ |
デフォルト値 |
順域 |
発効方法 |
意味 |
|---|---|---|---|---|
| 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();
以下の表は、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オブジェクトを使用する場合:
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オブジェクトを使用する際のバッチ実行における動作の違いを示しています(前提条件は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はバッチ実行の性能をより効果的に発揮できるため、推奨される設定です。具体的には、以下の設定を使用します:
シナリオ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を特定する鍵は、論理読み取り行数、影響行数、返される行数の変動を監視することです。具体的な識別ルールは以下の通りです:
- 論理読み取り行数の変動が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 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つのノードに集中している場合。