SQL 支持和解决方法

由于 Citus 通过扩展 PostgreSQL 提供分布式功能,因此它与 PostgreSQL 结构兼容。 这意味着用户可以使用丰富且可扩展的 PostgreSQL 生态系统附带的工具和功能来处理使用 Citus 创建的分布式表。

Citus 对它能够在单个工作节点上执行的任何查询具有 100% 的 SQL 覆盖率。 当访问有关单个租户的信息时,此类查询在 多租户应用程序 中很常见。

甚至跨节点查询(用于并行计算)也支持大多数 SQL 功能。但是,组合来自多个节点的信息的查询不支持某些 SQL 功能。

跨节点 SQL 查询的限制:

  • SELECT … FOR UPDATE 仅在单分片查询中工作

  • TABLESAMPLE 仅在单分片查询中工作

  • 仅当关联位于 分布列 上时,才支持关联子查询。

  • 分布式表之间的外连接仅在 分布列 上受支持

  • 仅当分布式表在外侧时,才支持分布式表和引用表或本地表之间的外连接

  • Recursive CTEs 仅在单分片查询中工作

  • Grouping sets 仅在单分片查询中工作

要了解有关 PostgreSQL 及其功能的更多信息,您可以访问 PostgreSQL documentation。 有关 PostgreSQL SQL 命令方言(可供 Citus 用户按原样使用)的详细参考,您可以查看 SQL Command Reference

解决方法

在尝试解决方法之前,请考虑 Citus 是否适合您的情况。Citus 当前版本适用于 real-time analytics and multi-tenant use cases.

Citus 支持多租户用例中的所有 SQL 语句。 即使在跨节点查询的实时分析用例中,Citus 也支持大多数语句。 unsupported 中列出了几种不受支持的查询类型, 许多不受支持的功能都有解决方法;以下是一些最有用的。

使用 CTE 解决限制

当 SQL 查询不受支持时,解决它的一种方法是使用 CTE,它使用我们所谓的 pull-push 执行。

SELECT * FROM ref LEFT JOIN dist USING (id) WHERE dist.value > 10;
/*
ERROR:  cannot pushdown the subquery
DETAIL:  There exist a reference table in the outer part of the outer join
*/

要解决此限制,您可以通过将分布式部分包装在 CTE 中来将查询转换为路由器查询

WITH x AS (SELECT * FROM dist WHERE dist.value > 10)
SELECT * FROM ref LEFT JOIN x USING (id);

请记住,coordinator 会将 CTE 的结果发送给所有需要它进行处理的 worker。 因此,最好将最具体的过滤器和限制添加到内部查询中,或者聚合表。 这减少了此类查询可能导致的网络开销。在 subquery_perf 中了解更多信息。

临时表:不得已的解决方法

即使通过子查询使用推拉执行,仍有一些查询 unsupported。 其中之一是在分布式表上使用 grouping sets

在我们的 real-time analytics tutorial 中, 我们创建了一个名为 github_events 的表, 由 user_id 列分布。让我们查询它并找到一组预选的 repos 的最早事件,按事件类型(event type)和事件宣传(event publicity)的组合分组。 一种方便的方法是使用分组集。但是,如前所述,分布式查询尚不支持此功能:

-- this won't work

  SELECT repo_id, event_type, event_public,
         grouping(event_type, event_public),
         min(created_at)
    FROM github_events
   WHERE repo_id IN (8514, 15435, 19438, 21692)
GROUP BY repo_id, ROLLUP(event_type, event_public);
ERROR:  could not run distributed query with GROUPING
HINT:  Consider using an equality filter on the distributed table's partition column.

不过,有一个窍门。我们可以将相关信息作为临时表拉取到协调器:

-- grab the data, minus the aggregate, into a local table

CREATE TEMP TABLE results AS (
  SELECT repo_id, event_type, event_public, created_at
    FROM github_events
       WHERE repo_id IN (8514, 15435, 19438, 21692)
    );

-- now run the aggregate locally

  SELECT repo_id, event_type, event_public,
         grouping(event_type, event_public),
         min(created_at)
    FROM results
GROUP BY repo_id, ROLLUP(event_type, event_public);
.
 repo_id |    event_type     | event_public | grouping |         min
---------+-------------------+--------------+----------+---------------------
    8514 | PullRequestEvent  | t            |        0 | 2016-12-01 05:32:54
    8514 | IssueCommentEvent | t            |        0 | 2016-12-01 05:32:57
   19438 | IssueCommentEvent | t            |        0 | 2016-12-01 05:48:56
   21692 | WatchEvent        | t            |        0 | 2016-12-01 06:01:23
   15435 | WatchEvent        | t            |        0 | 2016-12-01 05:40:24
   21692 | WatchEvent        |              |        1 | 2016-12-01 06:01:23
   15435 | WatchEvent        |              |        1 | 2016-12-01 05:40:24
    8514 | PullRequestEvent  |              |        1 | 2016-12-01 05:32:54
    8514 | IssueCommentEvent |              |        1 | 2016-12-01 05:32:57
   19438 | IssueCommentEvent |              |        1 | 2016-12-01 05:48:56
   15435 |                   |              |        3 | 2016-12-01 05:40:24
   21692 |                   |              |        3 | 2016-12-01 06:01:23
   19438 |                   |              |        3 | 2016-12-01 05:48:56
    8514 |                   |              |        3 | 2016-12-01 05:32:54

在 coordinator 上创建临时表是最后的手段。它受节点的磁盘大小和 CPU 的限制。