Почему план выполнения с удаленного сервера в одной и той же базе данных postgres отличается?

#postgresql #remote-access

Вопрос:

У меня есть два сервера postgres с одинаковым IP-адресом и разными портами (одна и та же машина). Я использую postgres_fdw для связи баз данных — внешние таблицы из удаленной базы данных видны в схеме «eaps».

Запрос на объединение 5 таблиц: Таблицы: e_connector :

          |eap_id|start_object_id|end_object_id|skompo|ekompo|connector_type| 
 

..
Мне нужно отсканировать таблицу с включенным фильтром (eap_in,skompo,ekompo), а затем объединить таблицы e_object и c_package_tree, чтобы получить из них дополнительные данные для start_object_id и end_object_id.

e_объект :

       |eap_id|object_id|package_id |name|
 

c_package_tree :

       |eap_id|package_id |nametree|
 

Мне нужен результат:

 Select 
  tc.connector_type,skompo as start_kompo,ekompo as end_kompo,
  ts.name as sname, trs.nametree as snametree,
  te.name as ename, tre.nametree as enametree
from 
 e_connector  tc, e_object ts, e_object te, 
 c_package_tree trs , c_package_tree tre 
where 
    tc.eap_id=36 and 'OU' in (skompo,ekompo) and -- scan index
    tc.eap_id=ts.eap_id and tc.start_object_id=ts.object_id and -- with index
    tc.eap_id=te.eap_id and tc.end_object_id=te.object_id and -- with index
    tc.eap_id=trs.eap_id and ts.package_id=trs.package_id and -- with index
    tc.eap_id=tre.eap_id and te.package_id=tre.package_id  -- with index
 

Запрос должен быть изменен для поддержки первого сканирования e_connector, чтобы предотвратить огромное объединение в 4 других таблицах

 with con as (
    Select eap_id,tc.connector_type,skompo as start_kompo,ekompo as end_kompo,
    start_object_id,end_object_id
    from e_connector tc
    where 
    tc.eap_id=36 and 'OU' in (skompo,ekompo) ) 
Select tc.connector_type,start_kompo,end_kompo,
    ts.name as sname, trs.nametree as snametree,
    te.name as ename, tre.nametree as enametree
    from con tc, e_object ts, e_object te, c_package_tree trs , c_package_tree tre 
    where 
    tc.eap_id=ts.eap_id and tc.start_object_id=ts.object_id and 
    tc.eap_id=te.eap_id and tc.end_object_id=te.object_id and
    tc.eap_id=trs.eap_id and ts.package_id=trs.package_id and
    tc.eap_id=tre.eap_id and te.package_id=tre.package_id 
 

Это занимает 229 мсек в базе данных и сначала сканирует индекс e_connector (eap_id,skompo,ekompo), создает растровое изображение ИЛИ и выполняет внутренние соединения вложенного цикла с другими таблицами.
Один и тот же запрос с «eaps.» перед именами таблиц, выполняемый на удаленном сервере в одной и той же базе данных, имеет другой план выполнения для одних и тех же данных.

e_connector содержит 1142600 записей, 10500, в то время как отфильтрованный e_object содержит 1504650 записей, 63000 записей для данного eap_id и ~49800 из них имеют соединения

Индексы:

 Create unique index ie_object on e_object(eap_id,object_id);
Create unique index ic_package_tree on c_package_tree(eap_id,package_id);
Create index ix_connector_kompo on e_connector(eap_id,skompo,ekompo);
Create index ix_connector_start_end on e_connector(eap_id,start_object_id,end_object_id);
 

How can I avoid plan with :

  {
          "Node Type": "Foreign Scan",
          "Operation": "Select",
          "Parent Relationship": "Outer",
          "Parallel Aware": false,
          "Startup Cost": 100,
          "Total Cost": 311.73,
          "Plan Rows": 1,
          "Plan Width": 2088,
          "Output": [
            "ts.name",
            "ts.eap_id",
            "ts.object_id",
            "te.name",
            "te.eap_id",
            "te.object_id",
            "trs.nametree",
            "trs.eap_id",
            "tre.nametree",
            "tre.eap_id"
          ],
          "Relations": "(((eaps.e_object ts) 
          INNER JOIN (eaps.e_object te)) 
          INNER JOIN (eaps.c_package_tree trs)) 
          INNER JOIN (eaps.c_package_tree tre)",
          "Remote SQL": 
          "SELECT r2.name, r2.eap_id, r2.object_id, r3.name, r3.eap_id, r3.object_id, r4.nametree, r4.eap_id, r5.nametree, r5.eap_id 
          FROM (((public.e_object r2 
          INNER JOIN public.e_object r3 ON (((r2.eap_id = r3.eap_id)))) 
          INNER JOIN public.c_package_tree r4 ON (((r2.eap_id = r4.eap_id)) AND ((r2.package_id = r4.package_id)))) 
          INNER JOIN public.c_package_tree r5 ON (((r2.eap_id = r5.eap_id)) AND ((r3.package_id = r5.package_id))))"
        }
 

Мне нужно присоединиться к результату на удаленном сервере.

 with con as (
    Select eap_id,tc.ea_guid,tc.connector_type,skompo as start_kompo,ekompo as end_kompo,
    start_object_id,end_object_id
    from e_connector tc
    where 
    tc.eap_id=36 and 'OU' in (skompo,ekompo) ) 
Select tc.ea_guid,tc.connector_type,  end_kompo,
    ts.object_type as stype,ts.name as sname, ts.ea_guid as start_guid,trs.nametree as snametree,
    te.object_type as etype,te.name as ename, te.ea_guid as end_guid,tre.nametree as enametree
    from con  tc 
    join  e_object ts on (tc.eap_id=ts.eap_id and tc.start_object_id=ts.object_id)
    join e_object te on (tc.eap_id=te.eap_id and tc.end_object_id=te.object_id)
    join c_package_tree trs on (tc.eap_id=trs.eap_id and ts.package_id=trs.package_id)
    join c_package_tree tre on (tc.eap_id=tre.eap_id and te.package_id=tre.package_id)```
with join syntax
 

Комментарии:

1. from e_connector tc, e_object ts, e_object te, c_package_tree trs , c_package_tree tre <

2. с use_remote_estimate=true в качестве опции для иностранного сервера postgresql делает другой план выполнения — не лучший, но приемлемый.

3. синтаксис соединения или где синтаксис — тот же результат в плане выполнения. Соединения являются простыми eap_id и object_id для e_object и eap_id и package_id для c_package_tree. Версия с большим количеством подзапросов, определенных как со списком для объединения таблиц по таблицам, не влияет на план выполнения. Мой вопрос в том, почему разные планы. Тот же запрос с опцией postgres_fdw работает нормально, поэтому ответ — установить значение use_remote-оценка, но все равно это другой план, чем в базе данных.