Postgres_FDW不降低WHERE标准

Sve*_*cre 3 postgresql postgres-fdw postgres-9.6

我正在使用两个PostgreSQL 9.6数据库,正在尝试使用postgres_fdw从另一个数据库中查询一个数据库(一个是具有数据的生产备份DB,另一个是用于进行各种分析的数据库)。

我遇到了一些奇怪的行为,尽管查询中的某些类型的WHERE子句没有传递到远程数据库,而是保留在本地数据库中,并用于过滤从远程数据库接收的结果。这导致远程数据库尝试通过网络发送比本地数据库所需更多的信息,并且受影响的查询速度大大降低(15秒对15分钟)。

我主要通过与时间戳相关的子句看到了这一点,下面的示例是我第一次遇到此问题的方式,但是我在其他几种变体中也看到了它,例如用TIMESTAMP文字(慢速)替换CURRENT_TIMESTAMP或用TIME ZONE文字替换TIMESTAMP (快速)。

我缺少某个地方的设置可以对此有所帮助吗?我将其设置为团队使用混合级别的SQL背景,大多数人都不具备审查EXPLAIN计划之类的经验。我已经提出了一些解决方法(例如将相对时间子句放在sub-SELECT中),但是我不断遇到新的问题实例。

一个例子:

SELECT      var_1
           ,var_2
FROM        schema_A.table_A
WHERE       execution_ts <= CURRENT_TIMESTAMP - INTERVAL '1 hour'
        AND execution_ts >= CURRENT_TIMESTAMP - INTERVAL '1 week' - INTERVAL '1 hour'
ORDER BY    var_1
Run Code Online (Sandbox Code Playgroud)

解释计划

Sort  (cost=147.64..147.64 rows=1 width=1048)
  Output: table_A.var_1, table_A.var_2
  Sort Key: (table_A.var_1)::text
  ->  Foreign Scan on schema_A.table_A  (cost=100.00..147.63 rows=1 width=1048)
        Output: table_A.var_1, table_A.var_2
        Filter: ((table_A.execution_ts <= (now() - '01:00:00'::interval)) 
             AND (table_A.execution_ts >= ((now() - '7 days'::interval) - '01:00:00'::interval)))
        Remote SQL: SELECT var_1, execution_ts FROM model.table_A
                    WHERE ((model_id::text = 'ABCD'::text))
                      AND ((var_1 = ANY ('{1,2,3,4,5}'::bigint[])))
Run Code Online (Sandbox Code Playgroud)

上面的内容大约需要15-20分钟才能运行,而下面的内容只需几秒钟即可完成。

SELECT      var_1
           ,var_2
FROM        schema_A.table_A
WHERE       execution_ts <= (SELECT CURRENT_TIMESTAMP - INTERVAL '1 hour')
        AND execution_ts >= (SELECT CURRENT_TIMESTAMP - INTERVAL '1 week' - INTERVAL '1 hour')
ORDER BY    var_1
Run Code Online (Sandbox Code Playgroud)

解释计划

Sort  (cost=158.70..158.71 rows=1 width=16)
  Output: table_A.var_1, table_A.var_2
  Sort Key: table_A.var_1
  InitPlan 1 (returns $0)
    ->  Result  (cost=0.00..0.01 rows=1 width=8)
          Output: (now() - '01:00:00'::interval)
  InitPlan 2 (returns $1)
    ->  Result  (cost=0.00..0.02 rows=1 width=8)
          Output: ((now() - '7 days'::interval) - '01:00:00'::interval)
  ->  Foreign Scan on schema_A.table_A  (cost=100.00..158.66 rows=1 width=16)
        Output: table_A.var_1, table_A.var_2
        Remote SQL: SELECT var_1, var_2 FROM model.table_A
                    WHERE ((execution_ts <= $1::timestamp with time zone))
                      AND ((execution_ts >= $2::timestamp with time zone))
                      AND ((model_id::text = 'ABCD'::text))
                      AND ((var_1 = ANY ('{1,2,3,4,5}'::bigint[])))
Run Code Online (Sandbox Code Playgroud)

Lau*_*lbe 5

任何不是的功能IMMUTABLE都不会被按下。

见功能is_foreign_exprcontrib/postgres_fdw/deparse.c

/*
 * Returns true if given expr is safe to evaluate on the foreign server.
 */
bool
is_foreign_expr(PlannerInfo *root,
                RelOptInfo *baserel,
                Expr *expr)
{
...
    /*   
     * An expression which includes any mutable functions can't be sent over
     * because its result is not stable.  For example, sending now() remote
     * side could cause confusion from clock offsets.  Future versions might
     * be able to make this choice with more granularity.  (We check this last
     * because it requires a lot of expensive catalog lookups.)
     */
    if (contain_mutable_functions((Node *) expr))
        return false;

    /* OK to evaluate on the remote server */
    return true;
}
Run Code Online (Sandbox Code Playgroud)

  • 一般来说oracle_fdw比postgres_fdw能下推的东西少,因为Oracle和PostgreSQL不同。但是,我确实将“current_timestamp”和朋友们推下。从概念上讲,这可能是一个糟糕的决定,因为系统上的时钟可能不同,但我认为这是人们期望发生的事情。没有人关心这样的查询中的精确微秒! (2认同)