控制联接输入

默认情况下,优化器使用自己的内部逻辑来确定将一个表作为内部输入还是外部输入联接到另一个表。有时,优化器可能选择将更大的表作为联接的内部输入。但这样做会导致性能和并发问题。

如果配置参数 EnableForceOuter 设置为 1,则您可以通过 ALTER TABLE..FORCE OUTER 控制特定表的联接输入。FORCE OUTER 选项会在 TABLES 系统表中修改表的 force_outer 设置。实施联接时,Vertica 会比较参与联接的表的 force_outer 设置:

  • 如果表设置不同,Vertica 会使用它们来设置联接输入:

    • 相比其他表具有较低 force_outer 设置的表会作为内部输入联接到这些表。

    • 相比其他表具有较高 force_outer 设置的表会作为外部输入联接到这些表。

  • 如果所有表设置都相同,Vertica 会忽略它们,然后自行决定如何构建联接。

对于所有新定义的表,force_outer 列最初都设置为 5。您可以使用 ALTER TABLE..FORCE OUTERforce_outer 重置为等于或大于 0 的值。例如,您可以将 abcxyz 表的 force_outer 设置分别改为 3 和 8:

=> ALTER TABLE abc FORCE OUTER 3;
=> ALTER TABLE xyz FORCE OUTER 8;

根据这些设置,优化器会将 abc 作为内部输入联接到 force_outer 值大于 3 的任何表。优化器会将 xyz 作为外部输入联接到 force_outer 值小于 8 的任何表。

投影继承

直接查询投影时,它会继承其锚表的 force_outer 设置。然后查询会在联接到另一个投影后使用此设置。

启用强制联接输入

配置参数 EnableForceOuter 决定了 Vertica 是否使用表的 force_outer 值来实施联接。默认情况下,此参数设置为 0,并且强制联接输入处于禁用状态。您可以通过 ALTER SESSIONALTER DATABASE 分别在会话和数据库范围中启用强制联接输入:

=> ALTER SESSION SET EnableForceOuter = { 0 | 1 };
=> ALTER DATABASE db-name SET EnableForceOuter =  { 0 | 1 };

如果 EnableForceOuter 设置为 0,则 ALTER TABLE..FORCE OUTER 语句会返回以下警告:

查看强制联接输入

EXPLAIN 生成的查询计划会指示 EnableForceOuter 配置参数是否已启用。联接查询可能会包含 force_outer 设置小于或大于默认值 5 的表。在这种情况下,查询计划会包含相关联接查询的 Force outer level 字段。

例如,以下查询联接了 store.store_sales 表和 public.products 表,但这两个表具有相同的 force_outer 设置 (5)。EnableForceOuter已启用,如生成的查询计划中所示:

=> EXPLAIN SELECT s.store_key, p.product_description, s.sales_quantity, s.sale_date
   FROM store.store_sales s JOIN public.products p ON s.product_key=p.product_key
   WHERE s.sale_date='2014-12-01' ORDER BY s.store_key, s.sale_date;

 EnableForceOuter is on
 Access Path:
 +-SORT [Cost: 7K, Rows: 100K (NO STATISTICS)] (PATH ID: 1)
 |  Order: sales.store_key ASC, sales.sale_date ASC
 |  Execute on: All Nodes
 | +---> JOIN HASH [Cost: 5K, Rows: 100K (NO STATISTICS)] (PATH ID: 2) Outer (BROADCAST)(LOCAL ROUND ROBIN)
 | |      Join Cond: (sales.product_key = products.product_key)
 | |      Execute on: All Nodes
 | | +-- Outer -> STORAGE ACCESS for sales [Cost: 2K, Rows: 100K (NO STATISTICS)] (PATH ID: 3)
 | | |      Projection: store.store_sales_b0
 | | |      Materialize: sales.sale_date, sales.store_key, sales.product_key, sales.sales_quantity
 | | |      Filter: (sales.sale_date = '2014-12-01'::date)
 | | |      Execute on: All Nodes
 | | +-- Inner -> STORAGE ACCESS for products [Cost: 177, Rows: 60K (NO STATISTICS)] (PATH ID: 4)
 | | |      Projection: public.products_b0
 | | |      Materialize: products.product_key, products.product_description
 | | |      Execute on: All Nodes

以下 ALTER TABLE 语句将 public.productsforce_outer 设置重置为 1:

=> ALTER TABLE public.products FORCE OUTER 1;
ALTER TABLE

此时,为上述联接重新生成的查询包含 Force outer level 字段,并将 public.products 指定为内部输入:

=> EXPLAIN SELECT s.store_key, p.product_description, s.sales_quantity, s.sale_date
   FROM store.store_sales s JOIN public.products p ON s.product_key=p.product_key
   WHERE s.sale_date='2014-12-01' ORDER BY s.store_key, s.sale_date;

 EnableForceOuter is on
 Access Path:
 +-SORT [Cost: 7K, Rows: 100K (NO STATISTICS)] (PATH ID: 1)
 |  Order: sales.store_key ASC, sales.sale_date ASC
 |  Execute on: All Nodes
 | +---> JOIN HASH [Cost: 5K, Rows: 100K (NO STATISTICS)] (PATH ID: 2) Outer (BROADCAST)(LOCAL ROUND ROBIN)
 | |      Join Cond: (sales.product_key = products.product_key)
 | |      Execute on: All Nodes
 | | +-- Outer -> STORAGE ACCESS for sales [Cost: 2K, Rows: 100K (NO STATISTICS)] (PATH ID: 3)
 | | |      Projection: store.store_sales_b0
 | | |      Materialize: sales.sale_date, sales.store_key, sales.product_key, sales.sales_quantity
 | | |      Filter: (sales.sale_date = '2014-12-01'::date)
 | | |      Execute on: All Nodes
 | | +-- Inner -> STORAGE ACCESS for products [Cost: 177, Rows: 60K (NO STATISTICS)] (PATH ID: 4)
 | | |      Projection: public.products_b0
 | | |      Force outer level: 1
 | | |      Materialize: products.product_key, products.product_description
 | | |      Execute on: All Nodes

如果将 public.productsforce_outer 设置更改为 8,Vertica 会创建将 public.products 指定为外部输入的另一个查询计划:

=> ALTER TABLE public.products FORCE OUTER 8;
ALTER TABLE

=> EXPLAIN SELECT s.store_key, p.product_description, s.sales_quantity, s.sale_date
   FROM store.store_sales s JOIN public.products p ON s.product_key=p.product_key
   WHERE s.sale_date='2014-12-01' ORDER BY s.store_key, s.sale_date;


EnableForceOuter is on
 Access Path:
 +-SORT [Cost: 7K, Rows: 100K (NO STATISTICS)] (PATH ID: 1)
 |  Order: sales.store_key ASC, sales.sale_date ASC
 |  Execute on: All Nodes
 | +---> JOIN HASH [Cost: 5K, Rows: 100K (NO STATISTICS)] (PATH ID: 2) Inner (BROADCAST)
 | |      Join Cond: (sales.product_key = products.product_key)
 | |      Materialize at Output: products.product_description
 | |      Execute on: All Nodes
 | | +-- Outer -> STORAGE ACCESS for products [Cost: 20, Rows: 60K (NO STATISTICS)] (PATH ID: 3)
 | | |      Projection: public.products_b0
 | | |      Force outer level: 8
 | | |      Materialize: products.product_key
 | | |      Execute on: All Nodes
 | | |      Runtime Filter: (SIP1(HashJoin): products.product_key)
 | | +-- Inner -> STORAGE ACCESS for sales [Cost: 2K, Rows: 100K (NO STATISTICS)] (PATH ID: 4)
 | | |      Projection: store.store_sales_b0
 | | |      Materialize: sales.sale_date, sales.store_key, sales.product_key, sales.sales_quantity
 | | |      Filter: (sales.sale_date = '2014-12-01'::date)
 | | |      Execute on: All Nodes

限制

Vertica 在执行以下操作时会忽略 force_outer 设置:

  • 外联接:无论已联接表的 force_outer 设置如何,Vertica 通常都会遵循 OUTER JOIN 子句。

  • MERGE 语句联接。

  • 包含 SYNTACTIC_JOIN 提示的查询。

  • 半联接查询,例如 SEMI JOIN

  • 联接到子查询,其中有一个子查询总是按 force_outer 设置为 5 进行处理,而不论在此子查询中已联接表的 force_outer 设置如何。此设置决定了相对于其他联接输入,将子查询指定为内部还是外部输入。如果联接两个子查询,优化器会决定哪个是内部输入,哪个是外部输入。