Postgres_FDW не отталкивает ГДЕ - PullRequest
0 голосов
/ 04 мая 2018

Я работаю с двумя базами данных PostgreSQL 9.6 и пытаюсь запросить одну из БД у другой, используя postgres_fdw (одна - рабочая резервная БД, содержащая данные, а другая - БД для различных анализов).

Я столкнулся с некоторым странным поведением, когда некоторые типы предложений WHERE в запросе не передаются на удаленную БД, а вместо этого сохраняются в локальной БД и используются для фильтрации результатов, полученных от удаленной БД. Это приводит к тому, что удаленная БД пытается отправить по сети больше информации, чем требуется локальной БД, и затронутые запросы значительно медленнее (15 секунд против 15 минут).

В основном я видел это с предложениями, относящимися к отметке времени, приведенные ниже примеры показывают, как я впервые столкнулся с проблемой, но я видел это в нескольких других вариантах, таких как замена CURRENT_TIMESTAMP на литерал TIMESTAMP (медленный) или TIMESTAMP WITH ВРЕМЯ ЗОНА буквально (быстро).

Есть ли где-то пропущенная настройка, которая поможет с этим? Я настраиваю это для команды, чтобы использовать со смешанным уровнем фона 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

Объяснить план

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[])))

Для выполнения вышеизложенного требуется около 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

Объяснить план

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[])))

Ответы [ 2 ]

0 голосов
/ 04 мая 2018

Любая функция, отличная от IMMUTABLE, не будет нажата.

См. Функцию is_foreign_expr в contrib/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;
}
0 голосов
/ 04 мая 2018

Я думаю, что проблема может быть в контенте выполнения now() (который CURRENT_TIMESTAMP разрешает).
valur, возвращаемое now(), является фиксированным для текущей транзакции - это означает, что она должна выполняться локально.

оборачивая его, подвыбор приводит к постоянному значению timestamptz разрешение оценки может быть выполнено дистанционно.

с использованием постоянной метки времени, необходимой для преобразования между меткой времени и меткой времени, которая выполняется с использованием текущих правил часовых поясов (согласно SET TIME ZONE TO ....), и если база данных выбирает преобразование удаленного timestamptz в местное время для сравнения с timestamp буквально снова это должно быть сделано локально.

в целом timestamp следует избегать (используйте вместо него timestamptz), за исключением случаев, когда вы

  1. хочет, чтобы значения следовали за любыми изменениями, внесенными в летнее время правила и
  2. уверены, что вы никогда не захотите представлять временные метки в течение добавленного часа осенью
...