このドキュメントでは、LindormTable (ワイドテーブルエンジン) で Lindorm SQL を使用する際の一般的な問題と解決策を説明します。
このページに記載されているすべての問題は、LindormTable にのみ適用されます。
クエリに関する問題
-
Q: 非効率なクエリを解決または回避するにはどうすればよいですか?
A: クエリが
This query may be a full table scan and thus may have unpredictable performanceというエラーを返す場合、そのクエリは非効率なクエリと見なされます。非効率なクエリとは何ですか? LindormTable では、フィルター条件でプライマリキーや既存のインデックスを効果的に使用できず、フルテーブルスキャンが発生するクエリは、非効率なクエリと見なされます。LindormTable は、データベースのパフォーマンスと安定性を保護するため、デフォルトでこれらのクエリをブロックします。
マッチングルールは、MySQL が複合インデックスで使用するルールと同じく、最左プレフィックスの原則に従います。システムは、WHERE 句のカラムをプライマリキー (またはインデックスキー) のカラムと左端から順に照合します。クエリが最初のカラムをスキップすると、キーは使用されず、フルテーブルスキャンが発生します。
たとえば、テーブル
testに複合主キー (p1,p2,p3) があり、次を実行するとします。SELECT * FROM test WHERE p2 < 30;クエリは
p1をスキップするため、LindormTable は主キーを使用できません。p2 < 30という条件を満たすために、テーブル全体がスキャンされます。修正・回避策:
-
最左プレフィックスの原則に従い、WHERE 句にプライマリキーの最初のカラムを含めます。
-
テーブルのプライマリキーを再設計します。詳細については、「ワイドテーブルのプライマリキーの設計方法」をご参照ください。
-
クエリ対象のカラムにセカンダリインデックスを作成します。詳細については、「セカンダリインデックス」をご参照ください。
-
複数カラムにまたがる多次元クエリの場合は、検索インデックスを作成します。詳細については、「検索インデックス」をご参照ください。
-
非効率なクエリを強制的に実行するには、
/*+ _l_allow_filtering_ */ヒントを追加します:SELECT /*+ _l_allow_filtering_ */ * FROM dt WHERE nonPK = 100;
重要フルテーブルスキャンを強制すると、データベース全体のパフォーマンスと安定性が低下するリスクがあります。影響を評価した上で、このアプローチを使用してください。
-
-
Q: GROUP BY クエリが "subPlan groupby keys" エラーで失敗するのはなぜですか?
A: エラーメッセージ:
The diff group keys of subPlan is over lindorm.aggregate.subplan.groupby.keys.limit=..., it may cost a lot memory so we shutdown this SubPlanGROUP BY 操作によって生成されるグループが多すぎるためです。大量のグループはメモリを過剰に消費し、インスタンスの負荷を増大させるため、LindormTable はサブプランをシャットダウンします。
解決策:
-
集計前にフィルター条件を追加して、グループ数を減らします。
-
多次元集計シナリオの場合は、検索インデックスを作成して作業をオフロードします。詳細については、「検索インデックス」をご参照ください。
-
グループ数のしきい値を引き上げるには、Lindorm テクニカルサポート (DingTalk ID: s0s3eg3) にお問い合わせください。
重要グループ数のしきい値を引き上げると、メモリ消費量が増加し、インスタンスの安定性に影響する可能性があります。変更を行う前に、影響を評価してください。
-
-
Q: 動的列テーブルに対して
SELECT *を実行すると、なぜ "Limit not set" エラーで失敗するのですか?A: エラーメッセージ:
Limit of this select statement is not set or exceeds config when select all columns from table with property DYNAMIC_COLUMNS=true動的列を持つテーブルは固定スキーマを持たず、大量かつ予測不可能な数の列を含むことがあります。行数制限なしで
SELECT *を実行すると、高い I/O が発生してインスタンスの負荷が増加するため、LindormTable ではこのようなクエリに LIMIT 句が必須です。SELECT 文に LIMIT 句を追加してください。
SELECT * FROM test LIMIT 10; -
Q: クエリが "Code grows beyond 64 KB" エラーで失敗するのはなぜですか?
A: Lindorm SQL エンジンは Just-In-Time (JIT) コンパイルを使用します。これは、クエリの物理プランからバイトコードを生成し、実行時にコンパイルする仕組みです。このエラーは、生成されたメソッドのバイトコードが、Java 仮想マシン (JVM) が課す 64 KB の制限を超えたことを意味します。
最も一般的な原因は、SQL クエリ文の述語が長すぎたり、複雑すぎたりして、実行するには大きすぎるバイトコードが生成されることです。
SQL 文の述語式を単純化してください。複雑な条件をより小さな部分に分割するか、ロジックを書き換えてバイトコードのサイズを削減してください。
-
Q: クエリが "The estimated memory used by the query exceeds the maximum limit" エラーで失敗するのはなぜですか?
A: SQL エンジンは、結果セットの処理時 (集計、ソート、重複排除など) に大量のメモリを消費します。Lindorm SQL は高い同時実行性が求められるオンラインワークロード向けに設計されているため、デフォルトでは各クエリのメモリ使用量を 8 MB に制限しています。この制限を超えると、メモリオーバーフロー例外が発生します。
ステップ 1 — 事前診断:
実行計画を確認して、集計およびソート演算子がストレージエンジンにプッシュダウンされているか、SQL エンジンで実行されているかを判断します。詳細については、「実行計画の解釈」をご参照ください。
-
重い演算子が SQL エンジンで実行されている場合は、クエリの最適化が適切な対処法です (オプション 1 を参照)。
-
演算子がすでにプッシュダウンされており、クエリが最適化済みである場合は、メモリ制限を引き上げます (オプション 2 を参照)。
オプション 1 — クエリの最適化 (推奨):
インデックスを使用して集計とソートをストレージエンジンにプッシュダウンし、フィルター条件を厳しくして SQL エンジンが処理するデータ量を削減します。
オプション 2 — メモリ制限の引き上げ:
クエリがすでに最適化されており、より高い上限が必要な場合は、ALTER SYSTEM を使用して
QUERY_MAX_MEMを調整します:ALTER SYSTEM SET QUERY_MAX_MEM = 8388608;SHOW VARIABLES 文で現在の値を確認できます。
SQL エンジンのバージョンが 2.9.6.0 より前の場合は、Lindorm テクニカルサポート (DingTalk ID: s0s3eg3) に連絡して制限を引き上げてください。
重要高コンカレンシー環境では、
QUERY_MAX_MEMを引き上げると、クラスターへのメモリ負荷が高まり、強制的な Full GC がトリガーされ、クラスター全体の応答性が低下する可能性があります。この値を大きくする前に、クエリのスループットと同時実行性を慎重に評価してください。 -
-
Q: 1 つの WHERE 句で多くの
IS NULL条件を使用することが推奨されないのはなぜですか?A: LindormTable では、
IS NULLは、「列は存在するが値が NULL である」状態と「列が存在しない、または書き込まれていない」状態の両方を処理する必要があります。SQL ステートメントに複数のIS NULL述語が含まれている場合、SQL エンジンはコンパイル中にこれらの条件を組み合わせに展開することがあります。N 個のIS NULL述語は、理論上、最大で2^Nの分岐を生成する可能性があり、コンパイル時間とメモリ使用量が大幅に増加します。深刻な場合、これはクエリの実行とインスタンスの安定性に影響を及ぼす可能性があります。推奨事項: 単一の SELECT、UPDATE、または DELETE ステートメントで、多数の
column IS NULL条件を組み合わせることは避けてください。以下の方法をご検討ください。-
可能な限り、プライマリキー、セカンダリインデックス、または検索インデックスを使用してクエリ範囲を絞り込んでください。
-
ビジネスロジックでフィールドが空か存在するかを頻繁にチェックする必要がある場合は、書き込み側でそのビジネス状態を明示的に表現してください (例えば、デフォルト値やステータスカラムを使用)。
-
バッチ処理の場合は、作業をより単純な条件を持つ複数の SQL 文に分割するか、まず完全なプライマリキーを取得し、プライマリキーに基づいてバッチで処理してください。
-
-
Q: WHERE 句で AND で結合された多数の OR グループを使用することが推奨されないのはなぜですか?
問題:
WHERE 句が複数の括弧で囲まれた OR グループを AND で接続する SQL 文。例:SELECT * FROM orders WHERE (status = 1 OR status = 2) AND (pay_type = 'wechat' OR pay_type = 'alipay') AND (region = 'CN' OR region = 'US') AND ...;ネストが深くなるほど、リスクは大きくなります。これは SELECT、UPDATE、DELETE 文に同様に適用されます。
A: クエリを実行する前に、オプティマイザーは、個々の独立した条件に対して最適なインデックスアクセスパスを選択できるように、WHERE 句を AND 条件の OR セットである選言標準形 (DNF) に変換する必要があります。オプティマイザーが
(A OR B) AND (C OR D)に遭遇すると、分配法則を適用してそれを展開する必要があります。(A∨B)∧(C∨D) ⇒ (A∧C)∨(A∧D)∨(B∧C)∨(B∧D)(A or B) and (C or D) ⇒ (A and C) or (A and D) or (B and C) or (B and D)-
それぞれ 2 つの分岐を持つ 2 つのグループ:2×2 = 4 つの組み合わせ項。
-
それぞれ 2 つの分岐を持つ 3 つのグループ:2×2×2 = 8 つの組み合わせ項。
グループの数とグループあたりの分岐数が増えるにつれて、組み合わせ項の数は指数関数的に増加します。これにより、オプティマイザのコンパイル時間とメモリ使用量が大幅に増加し、深刻な場合にはクエリの実行とインスタンスの安定性に影響を与える可能性があります。
推奨事項:
-
1 つの SQL 文で、括弧で囲まれた多くの OR グループを AND で結合することは避けてください。
-
展開されるブランチの数を削減するには、同じ列の OR 条件を
IN(たとえばstatus IN (1, 2)) を使用して書き換えます。 -
可能な限り、プライマリキー、セカンダリインデックス、または検索インデックスを使用してクエリ範囲を絞り込んでください。
-
多次元マッチングシナリオでは、検索インデックスを使用してください。詳細については、「検索インデックス」をご参照ください。
-
ビジネスロジックで複雑な条件が必要な場合は、作業を複数のより単純な SQL 文に分割するか、まず完全なプライマリキーを取得し、プライマリキーに基づいてバッチで処理してください。
-
-
Q: 特定のクエリのメモリ制限エラーを診断し、解決するにはどうすればよいですか?
A: クエリがメモリ制限エラーで失敗した場合、以下の手順に従って根本原因を診断し、適切な解決策を選択してください。
ステップ 1 — 実行計画の確認:
失敗しているクエリに対して
EXPLAINを実行すると、その実行計画を表示できます。 詳細については、「実行計画の解釈」をご参照ください。実行計画で、メモリを大量に消費する演算子 (集計、ソート、重複排除など) が SQL エンジンで実行されているか、ストレージエンジンにプッシュダウンされているかを確認します。SQL エンジンで実行される演算子はクエリごとの制限からメモリを消費しますが、プッシュダウンされた演算子は代わりにストレージエンジンのリソースを使用します。
ステップ 2 — クエリの最適化を試みる:
メモリを大量に消費する演算子が SQL エンジンで実行されている場合は、次のアプローチを試してメモリ消費量を削減してください。
-
集計演算子の場合(
GROUP BY、COUNT、SUM、AVG):グループ化または集計される列にセカンダリインデックスまたは検索インデックスを作成し、ストレージエンジンへの演算子プッシュダウンを有効にします。 -
ソート演算子 (
ORDER BY) では、インメモリソートを回避するためにソート列を既存のインデックスと一致させるか、あるいはより厳密なWHERE条件を追加してソート前にデータセットサイズを削減してください。 -
重複排除演算子 (
DISTINCT) には、高カーディナリティ列に対するクエリで検索インデックスを使用するか、選択的なフィルター条件を追加して処理される行数を削減します。
ステップ 3 — 必要に応じてメモリ制限を引き上げる:
すべての演算子がすでにプッシュダウンされており、クエリをこれ以上最適化できない場合は、ALTER SYSTEM を使用して
QUERY_MAX_MEMの値を引き上げます:ALTER SYSTEM SET QUERY_MAX_MEM = <new_value_in_bytes>;SHOW VARIABLES 文で現在の値を確認できます。
重要高コンカレンシー環境では、
QUERY_MAX_MEMの値を大きくすると、クラスター全体のメモリープレッシャーが増加し、強制的な Full GC がトリガーされてクラスター全体の応答性が低下する可能性があります。この値を変更する前に、クエリのスループットとコンカレンシーを評価してください。 -
インデックスとスキーマに関する問題
-
Q: セカンダリインデックスの作成が "Executing job number exceed, max job number = 8" エラーで失敗するのはなぜですか?
A: 各 Lindorm インスタンスでは、同時に実行できるセカンダリインデックス構築タスクは最大 8 つまでです。すでに 8 つのタスクが実行中の場合、新しいインデックス作成の試みは失敗します。
多数のセカンダリインデックスを同時に作成することは避けてください。多数のインデックスを一括で作成する必要がある場合は、Lindorm テクニカルサポート (DingTalk ID: s0s3eg3) にお問い合わせください。
-
Q: カラムを削除した後、同じ名前のカラムを再追加しようとすると "column is under deleting" エラーで失敗するのはなぜですか?
A: カラムを削除すると、LindormTable はメモリ、ホットストレージ、コールドストレージからカラムのデータを非同期にクリーンアップします。システムは、型の不一致やデータ衝突によるダーティデータを避けるため、クリーンアップが完了するまで同じ名前のカラムの再追加を防ぎます。
クリーンアップはバックグラウンドで実行され、長時間かかる場合があります。これを高速化するには、テーブルに対して次のステートメントを実行してください (
dtをご使用のテーブル名に置き換えてください):-- 残留するメモリ内データをストレージにフラッシュします。 ALTER TABLE dt FLUSH; -- コンパクションを実行して、削除されたデータをマージおよび削除します。 ALTER TABLE dt COMPACT;クリーンアップが完了したら、カラムを再追加できるようになります。
重要-
FLUSHは SQL エンジン バージョン 2.7.1 以降でサポートされています。お使いのバージョンは SQL バージョンガイド でご確認ください。 -
FLUSHとCOMPACTは両方とも非同期です。ステートメントが正常に実行されても、クリーンアップが完了したことを意味するものではありません。 -
データ量が多いテーブルで
COMPACTを実行すると、大量のシステムリソースを消費します。 業務のピーク時間帯に実行することは避けてください。
-
-
Q: セカンダリインデックスを作成した後、データの書き込みが "User-Defined-Timestamp" エラーで失敗するのはなぜですか?
A: エラーメッセージ:
Performing put operations with User-Defined-Timestamp in indexed column on MULTABLE_LATEST table is unsupportedUPSERT 文で
/*+ _l_ts */ヒントを使用する場合など、明示的なカスタムタイムスタンプでデータを書き込むには、プライマリテーブルとセカンダリインデックステーブルの両方で、可変性をMUTABLE_ALLに設定する必要があります。しかし、Lindorm では、パフォーマンス上の理由から、新しいテーブルとインデックスはデフォルトでMUTABLE_LATESTに設定されます。可変性がMUTABLE_LATESTであるインデックステーブルにカスタムタイムスタンプで書き込むと、このエラーが発生します。MUTABILITY プロパティは、インデックステーブルが作成された後は変更できません。既存のインデックスを削除し、プライマリテーブルの MUTABILITY を更新してから、インデックスを再作成する必要があります。
-
既存のセカンダリインデックスを無効にして削除してください。
-- インデックスを無効にします。 ALTER INDEX IF EXISTS <original_secondary_index_name> ON <primary_table_name> DISABLED; -- インデックスを削除します。 DROP INDEX IF EXISTS <original_secondary_index_name> ON <primary_table_name>;詳細については、「セカンダリインデックスの削除」をご参照ください。
-
プライマリテーブルの可変性を
MUTABLE_ALLに設定します:ALTER TABLE IF EXISTS <primary_table_name> SET MUTABILITY='MUTABLE_ALL'; -
新しいセカンダリインデックスを作成してください。詳細については、「CREATE INDEX」をご参照ください。
説明カスタムタイムスタンプでのデータ書き込みの詳細については、「HINT を使用して多バージョンデータ管理のタイムスタンプを設定する」をご参照ください。
説明セカンダリインデックスの MUTABILITY がカスタムタイムスタンプとどのように相互作用するかの詳細については、「カスタムタイムスタンプでインデックスを更新する」をご参照ください。
-