SQL クエリに DISTINCT オペレーターが含まれている場合、データベースはソートまたはハッシュ化を使用して重複行を削除します。この操作は、特に大規模なデータセットの場合、多くの CPU およびメモリリソースを消費する可能性があります。多くのシナリオでは、SELECT リストにプライマリキーが含まれている場合など、クエリ結果はすでに一意です。このような場合、DISTINCT 操作は冗長になります。PolarDB for MySQL のオプティマイザーは、テーブルスキーマとクエリ条件の関数従属性を分析することで、冗長な DISTINCT 操作を識別して排除します。この機能は、不要な計算を削減し、クエリの実行時間、CPU 使用量、メモリ使用量を低減します。
仕組み
この最適化の中核となるのは、関数従属性の概念です。関数従属性とは、テーブルの 1 つ以上の列の値が、他の 1 つ以上の列の値を一意に決定することを意味します。例えば、テーブルのプライマリキーは、そのテーブル内の他のすべての列を一意に決定します。
オプティマイザーが DISTINCT を含むクエリを分析する際、プライマリキーや一意なインデックスなどのテーブルのメタデータとクエリ条件を使用して一意性を推論します。オプティマイザーが射影列 (SELECT 列) の列の組み合わせがすでに一意な結果を保証すると判断した場合、実行計画から DISTINCT オペレーターを削除します。
適用範囲
クラスターバージョン:
製品シリーズ: Cluster Edition、Standard Edition。
カーネルバージョン: MySQL 8.0.2、リビジョン 8.0.2.2.31.1 以降。
機能範囲:
射影列に一意キーが含まれる場合:
SELECT文の列にテーブルの一意キー (プライマリキーまたは一意なインデックスを構成するすべての列) が含まれている場合、結果セットの一意性が保証されます。-- user_id が users テーブルのプライマリキーであると仮定します SELECT DISTINCT user_id, user_name FROM users; -- DISTINCT は排除可能です射影列が関数従属的に一意キーに依存する場合:
SELECT文のすべての列が一意キーによって一意に決定される場合、クエリ結果も一意になります。これは通常、単一テーブルのクエリや、ファクトテーブルとの 1 対 1 または多対 1 の結合で発生します。-- user_id がプライマリキーであり、(user_name, email) が user_id によって一意に決定されると仮定します SELECT DISTINCT user_name, email FROM users WHERE user_id = 123; -- DISTINCT は排除可能ですすべての射影列が定数である場合:
SELECT文のすべての列が定数である場合、結果セットには最大で 1 行しか含まれません。この場合、DISTINCTの使用は冗長です。オプティマイザーはDISTINCTを削除し、LIMIT 1を追加します。SELECT DISTINCT 'hello', 123 FROM t1 WHERE a = 1; -- DISTINCT は排除可能で、LIMIT 1 が追加されますUNION DISTINCTクエリ:UNION DISTINCTクエリにおいて、UNION内の各SELECT文の結果セットがすでに一意である場合、オプティマイザーはこれらの内部SELECT文に対するDISTINCT操作を排除できます。
説明オプティマイザーは、一意性を推論する際に
NOT NULL制約を厳密に考慮します。一意キーの列がNULLである可能性がある場合、一意性の判断に影響を与え、オプティマイザーがDISTINCTを排除できなくなることがあります。
DISTINCT 排除機能の有効化
distinct_elimination_mode パラメーターを設定して、この最適化機能の動作を制御します。
PolarDB クラスターのパラメーターを変更する方法は、コンソールとデータベースセッションで異なります。違いは次のとおりです:
PolarDB コンソール の場合
互換性: MySQL の構成ファイルとの互換性のため、PolarDB コンソールの一部のクラスターパラメーターには loose_ プレフィックスが付いています。
手順:
loose_プレフィックスが付いているパラメーターを見つけて変更します。
データベースセッション (コマンドラインまたはクライアントを使用) の場合
手順: データベースセッションで
SETコマンドを使用してパラメーターを変更する場合、loose_プレフィックスを削除し、元のパラメーター名を使用します。
パラメーター | レベル | 説明 |
| グローバル/セッション | この機能のメインスイッチです。有効な値は次のとおりです:
|
最適化シナリオの例
DISTINCT 排除機能が有効になっていることを確認するには、EXPLAIN コマンドを使用してクエリの実行計画を表示します。最適化が有効な場合、実行計画にはソートやハッシュベースの重複排除など、DISTINCT 処理のためのオペレーターは含まれません。以下のセクションでは、典型的な最適化シナリオについて説明します。
シナリオ 1:射影列が定数である場合
オプティマイザーが射影列が定数であることを識別すると、DISTINCT を排除し、LIMIT 1 を追加できます。
データの準備:
t1テーブルを作成し、データを挿入します。CREATE TABLE testdb.t1 (a INT, b INT); INSERT INTO testdb.t1 VALUES (1, 10), (1, 20);元のクエリの実行:
EXPLAIN SELECT DISTINCT a + 1 FROM testdb.t1 WHERE a = 1;最適化の検証: 最適化された実行計画で、
warningsの情報を確認します。DISTINCT操作が排除されています。SHOW warnings; +-------+------+-------------------------------------------------------------------------------------------------------------------+ | Level | Code | Message | +-------+------+-------------------------------------------------------------------------------------------------------------------+ | Note | 1003 | /* select#1 */ select (`testdb`.`t1`.`a` + 1) AS `a + 1` from `testdb`.`t1` where (`testdb`.`t1`.`a` = 1) limit 1 | +-------+------+-------------------------------------------------------------------------------------------------------------------+
シナリオ 2:関数従属性による一意性の推論
列 a が列 b を一意に決定し、かつ b が NULL でない一意キーである場合、オプティマイザーは a も一意であると推論できます。これにより、DISTINCT を排除できます。
データの準備:
t2テーブルを作成します。ここで、列bは列aから生成され、一意です。CREATE TABLE t2 ( a INT, b INT AS (a + 1) UNIQUE ); INSERT INTO t2(a) VALUES (10), (20);元のクエリの実行:
EXPLAIN SELECT DISTINCT a FROM t2 WHERE b IS NOT NULL;最適化の検証:
bの一意性が結果におけるaの一意性を保証するため、DISTINCTは削除されます。warningsの情報を確認します:SHOW warnings; +-------+------+---------------------------------------------------------------------------------------------------------+ | Level | Code | Message | +-------+------+---------------------------------------------------------------------------------------------------------+ | Note | 1003 | /* select#1 */ select `testdb`.`t2`.`a` AS `a` from `testdb`.`t2` where (`testdb`.`t2`.`b` is not null) | +-------+------+---------------------------------------------------------------------------------------------------------+
シナリオ 3:サブクエリの結果が自然に一意である場合
サブクエリ dt が一意キー c を含むため自然に一意である場合、JOIN 操作後も結果の一意性が保たれることがあります。これにより、オプティマイザーは外部の DISTINCT を排除できます。
データの準備:
t3テーブルを作成します。ここで、列cはaとbから生成され、一意です。CREATE TABLE t3 ( a INT NOT NULL, b INT NOT NULL, c INT AS (a + b) UNIQUE ); INSERT INTO t3(a, b) VALUES (1, 10), (2, 20);元のクエリの実行:
EXPLAIN SELECT DISTINCT a, b FROM (SELECT a, b, c FROM t3) dt, (SELECT a AS d FROM t3 limit 1) dt2 WHERE c IS NOT NULL;最適化の検証: 最適化された実行計画で、
warningsの情報を確認します。DISTINCT操作が排除されています。SHOW warnings; +-------+------+----------------------------------------------------------------------------------------------------------------------------------+ | Level | Code | Message | +-------+------+----------------------------------------------------------------------------------------------------------------------------------+ | Note | 1003 | /* select#1 */ select `testdb`.`t3`.`a` AS `a`,`testdb`.`t3`.`b` AS `b` from `testdb`.`t3` where (`testdb`.`t3`.`c` is not null) | +-------+------+----------------------------------------------------------------------------------------------------------------------------------+
パフォーマンス
DISTINCT 排除機能を有効にすると、典型的なシナリオで大幅なパフォーマンス向上が見られます:
メトリック | パフォーマンス向上 |
実行時間 | 10% から 60% 削減。 |
メモリ使用量 | 20% から 70% 削減。 |
CPU 使用率 | ハッシュ化またはソート操作による CPU オーバーヘッドを削減します。 |