全部產品
Search
文件中心

PolarDB:串連(JOIN)消除

更新時間:Dec 03, 2025

多表JOIN操作是複雜分析查詢中的常見效能瓶頸。PolarDB MySQL版的串連(JOIN)消除功能,可在查詢最佳化階段識別並移除不必要的JOIN操作,以簡化執行計畫、減少I/O和計算量,提升查詢效能。

適用範圍

  • 產品系列:叢集版標準版

  • 核心版本:MySQL 8.0.2,且修訂版本需為8.0.2.2.31.1及以上版本。

開啟串連(JOIN)消除

通過設定join_elimination_mode參數來控制此最佳化功能的行為。

PolarDB叢集參數在控制台與會話中修改方式存在差異,詳細區別如下:

  • PolarDB控制台上修改

    • 相容性說明:部分叢集參數在PolarDB控制台上均已添加MySQL設定檔的相容性首碼loose_

    • 操作方法:找到並修改這些帶loose_首碼的參數。

  • 在資料庫會話中修改(使用命令列或用戶端):

    • 操作方法:當您串連到資料庫,使用SET命令修改參數時,請去掉loose_首碼,直接使用參數的原始名稱進行修改。

參數名稱

層級

描述

loose_join_elimination_mode

Global/Session

控制該功能的主開關。取值範圍如下:

  • REPLICA_ON(預設值):僅在唯讀(RO)節點上開啟此功能。

  • ON:開啟。

  • OFF:關閉。

最佳化情境樣本

當前功能支援在以下六種情境中自動進行串連消除。

情境一:消除ON條件為常假的左串連(LEFT JOIN + FALSE COND)

LEFT JOINON條件恒為FALSE時,該串連無意義。最佳化器會移除內表,並將其所有列替換為NULL

-- 最佳化前
SELECT ..., ti1.*, ti2.*, ... FROM ... LEFT JOIN (ti1, ti2, ...) ON FALSE;
-- 最佳化後
SELECT ..., NULL, NULL, ... FROM ...;

消除條件

LEFT JOINON條件恒為FALSE的運算式(例如1=0FALSE)。

情境樣本

  1. 準備環境。

    DROP TABLE IF EXISTS orders;
    CREATE TABLE orders (
        order_id INT PRIMARY KEY,
        customer_id INT,
        order_date DATE
    );
    DROP TABLE IF EXISTS customers;
    CREATE TABLE customers (
        customer_id INT PRIMARY KEY,
        customer_name VARCHAR(100)
    );
    INSERT INTO orders VALUES (1, 101, '2023-10-01');
  2. 最佳化前:關閉JOIN消除,執行一個ON FALSE的查詢。

    SET SESSION join_elimination_mode = 'OFF';
    EXPLAIN SELECT o.*, c.* FROM orders o LEFT JOIN customers c ON FALSE;
    SHOW warnings;
    +-------+------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
    | Level | Code | Message                                                                                                                                                                                                                                                                                                                          |
    +-------+------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
    | Note  | 1003 | /* select#1 */ select `testdb`.`o`.`order_id` AS `order_id`,`testdb`.`o`.`customer_id` AS `customer_id`,`testdb`.`o`.`order_date` AS `order_date`,`testdb`.`c`.`customer_id` AS `customer_id`,`testdb`.`c`.`customer_name` AS `customer_name` from `testdb`.`orders` `o` left join `testdb`.`customers` `c` on(false) where true |
    +-------+------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
  3. 最佳化後:開啟JOIN消除,執行相同的查詢。

    SET SESSION join_elimination_mode = 'ON';
    EXPLAIN SELECT o.*, c.* FROM orders o LEFT JOIN customers c ON FALSE;
    SHOW warnings;
    +-------+------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
    | Level | Code | Message                                                                                                                                                                                                                    |
    +-------+------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
    | Note  | 1003 | /* select#1 */ select `testdb`.`o`.`order_id` AS `order_id`,`testdb`.`o`.`customer_id` AS `customer_id`,`testdb`.`o`.`order_date` AS `order_date`,NULL AS `customer_id`,NULL AS `customer_name` from `testdb`.`orders` `o` |
    +-------+------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+

    執行計畫顯示customers表的訪問被消除。

情境二:消除不影響結果的唯一鍵左串連 (LEFT JOIN + UNIQUE)

當左串連的內表未在查詢的其他部分被引用,且串連操作不改變外表的行數時,對內表的串連可以被安全地消除。

-- 最佳化前
SELECT to1.*, to2.*, ..., tom.* FROM to1, to2, ..., tom LEFT JOIN (ti1, ti2, ..., tin) ON cond_on WHERE cond_where ...
-- 最佳化後
SELECT to1.*, to2.*, ..., tom.* FROM to1, to2, ..., tom WHERE cond_where ...

消除條件

  • 內表未被引用:在LEFT JOIN及其ON條件以外的地方,都沒有引用內表(即ti1, ti2, ..., tin)的任何列。

  • 串連不影響外表基數或結果:需滿足以下條件之一。

    • 串連具有唯一性:對於外表的每一行,經過左串連後有且僅能匹配到內表的一行資料。

    • 串連產生的重複行對結果無影響:即使串連導致外表的行被複製,但查詢的後續操作會消除這些重複行的影響。常見情況包括:

      • 串連位於一個EXISTSIN子句(即半串連,SEMI JOIN)中。

      • 子查詢中包含了GROUP BYLIMIT或視窗函數等限制條件。

      • 遞迴使用上述兩項。

情境三:消除自串連(SELF JOIN)

當一個基表(或其派生表)與自身進行內串連(INNER JOIN)時,相當於用兩份相同或相似的資料集進行匹配。如果滿足特定條件,最佳化器可以識別出其中一份資料是多餘的,並將其從執行計畫中消除,從而避免對同一份資料的重複讀取。

  • 基表JOIN基表:

    -- 最佳化前
    SELECT target.*, source.* FROM t1 as target JOIN t1 as source WHERE target.uk = source.uk;
    -- 最佳化後
    SELECT source.*, source.* FROM source WHERE source.uk = source.uk;
  • 派生表JOIN派生表:

    -- 最佳化前
    SELECT target.*, source.* FROM (SELECT * FROM t1) target JOIN (SELECT * FROM t1 WHERE t1.a > 1) source WHERE target.uk = source.uk;
    -- 最佳化後
    SELECT source.*, source.* FROM (SELECT * FROM t1 WHERE t1.a > 1) source WHERE source.uk = source.uk;
  • 基表JOIN派生表:

    -- 最佳化前
    SELECT target.*, source.* FROM t1 target JOIN (SELECT * FROM t1 WHERE t1.a > 1) source WHERE target.uk = source.uk;
    -- 最佳化後
    SELECT source.*, source.* FROM (SELECT * FROM t1 WHERE t1.a > 1) source WHERE source.uk = source.uk;

消除條件

  • 子集關係source表的結果集需是target表結果集的子集。

  • 列可用性:查詢中所有被引用的targe表的列,在source表中也需存在。簡單來說,被保留的source表需包含查詢所需的所有列。

  • 唯一鍵串連JOIN的串連條件需是基於target表中的一個唯一鍵(UNIQUE KEY)或主鍵(PRIMARY KEY)的等值比較。

    • 這個條件是關鍵,它保證了source表中的每一行最多隻能匹配到target表中的一行。這確保了消除target表不會改變查詢結果的行數和邏輯。

    • 特殊情況:如果串連條件中沒有基於唯一鍵的等值比較,那麼只有當target表的結果集本身只有0行或1行時,才可能進行消除。

情境四:消除半自串連(SELF SEMI JOIN)

當一個表與自身進行半串連(SEMI JOIN,通常表現為INEXISTS子句)時,如果滿足特定條件,子查詢中的表可以被消除,其條件會被合并到外層查詢。

  • 有唯一列相等條件。

    -- 最佳化前
    SELECT source.* FROM t1 as source WHERE EXISTS (SELECT * FROM t1 as target WHERE source.uk = target.uk AND target.a > 1);
    -- 最佳化後
    SELECT source.* FROM t1 as source WHERE source.uk = source.uk AND source.a > 1;
  • 無唯一列相等條件。

    -- 最佳化前
    SELECT source.* FROM t1 as source WHERE EXISTS (SELECT * FROM t1 as target WHERE source.a = target.a);
    -- 最佳化後
    SELECT source.* FROM t1 as source WHERE source.a = source.a;

消除條件

  • 子集關係source表的結果集需是target表結果集的子集。

  • 列可用性:查詢中所有被引用的targe表的列,在source表中也需存在。簡單來說,被保留的source表需包含查詢所需的所有列。

  • 還需滿足以下條件之一:

    • 唯一鍵串連JOIN的串連條件需是基於target表中的一個唯一鍵(UNIQUE KEY)或主鍵(PRIMARY KEY)的等值比較。

      • 這個條件是關鍵,它保證了source表中的每一行最多隻能匹配到target表中的一行。這確保了消除target表不會改變查詢結果的行數和邏輯。

      • 特殊情況:如果串連條件中沒有基於唯一鍵的等值比較,那麼只有當target表的結果集本身只有0行或1行時,才可能進行消除。

    • 所有串連條件均為等值(即)串連,且與其他添加為AND串連。

情境五:消除外鍵串連(FOREIGN KEY JOIN)

如果兩表間存在外鍵約束,JOIN條件為該外鍵關係,且查詢只引用子表(含外鍵的表)的列,則對父表的JOIN可被消除。外鍵約束保證了子表中的行在父表中必有對應行。

CREATE TABLE target (a int primary key);
CREATE TABLE source (a int, foreign key (a) references target(a));

-- 最佳化前
SELECT target.a, source.a FROM target, source WHERE target.a = source.a;
-- 最佳化後
SELECT source.a, source.a FROM source WHERE source.a = source.a;

消除條件

  • 父表引用可替代:查詢所引用的所有父表列,都可以通過子表的對應外鍵列來替代(通常意味著只引用了父表的串連鍵本身)。

  • 串連條件基於外鍵關係JOIN的條件是子表的外鍵列與父表的主鍵/唯一鍵列的等值比較。

  • 外鍵非空保證:子表中的外鍵列被定義為NOT NULL,這確保了子表中的每一行在父表中都有確切的對應行。

情境六:消除外鍵半串連(FOREIGN KEY SEMI JOIN)

當使用EXISTSIN子句通過外鍵關係檢查父表是否存在對應記錄時,該檢查是多餘的,因為外鍵約束已保證記錄存在。因此,該SEMI JOIN子查詢可被消除。

CREATE TABLE target (a int primary key);
CREATE TABLE source (a int, foreign key (a) references target(a));

-- 最佳化前
SELECT source.a FROM source WHERE EXISTS(SELECT * FROM target WHERE target.a = source.a);
-- 最佳化後
SELECT source.a FROM source WHERE source.a = source.a;

消除條件

  • 父表引用可替代:查詢所引用的所有父表列,都可以通過子表的對應外鍵列來替代(通常意味著只引用了父表的串連鍵本身)。

  • 串連條件基於外鍵關係JOIN的條件是子表的外鍵列與父表的主鍵/唯一鍵列的等值比較。

  • 外鍵非空保證:子表中的外鍵列被定義為NOT NULL,這確保了子表中的每一行在父表中都有確切的對應行。

常見問題

開啟join_elimination_mod後,為何查詢未被最佳化?

按以下步驟排查:

  1. 檢查版本:確認叢集版本滿足適用範圍

  2. 檢查情境:確認SQL和表結構符合本文檔所述的六種最佳化情境之一的消除條件。

  3. 檢查SELECT列表:對於LEFT JOINFOREIGN KEY JOIN等情境,確保查詢的SELECT列表中未引用被消除表的任何列。

  4. 檢查查詢複雜度:部分複雜的查詢結構(如某些子查詢嵌套)可能阻止最佳化器進行消除。可嘗試簡化查詢進行測試。

此功能是否會影響資料一致性或查詢結果的正確性?

不會。JOIN消除是在保證查詢結果等價的前提下進行的邏輯最佳化。它只改變查詢的執行方式,不改變查詢的語義和資料結果。所有最佳化都經過嚴格的條件判斷,以確保結果正確。