Производительность подзапроса в простом случае - PullRequest
0 голосов
/ 07 октября 2018

Postgresql 10 - последняя версия Ubuntu LTS - 1CPU, 2 ГБ оперативной памяти - другой SW не установлен
Две таблицы, обе с индексами:
следует (22 записи)
советы (2,5 млн записей)

select users_id_to from follows where users_id_from =1 

занимает 0,041 мсек

select tips.id
from tips
where tips.users_id in (2,3,4,5,6,8,79407,38463,42798,94150,76554,56777,71407,51788,4624,41079,13549,75920,18979,6078,26178,18316) 

Bitmap Heap Scan on tips  (cost=101.72..2122.76 rows=556 width=8) (actual time=0.267..1.120 rows=597 loops=1)   
  Recheck Cond: (users_id = ANY ('{2,3,4,5,6,8,79407,38463,42798,94150,76554,56777,71407,51788,4624,41079,13549,75920,18979,6078,26178,18316}'::bigint[]))  
  Heap Blocks: exact=594    
  ->  Bitmap Index Scan on tips_idx_users_id01  (cost=0.00..101.58 rows=556 width=0) (actual time=0.188..0.188 rows=597 loops=1)    
        Index Cond: (users_id = ANY ('{2,3,4,5,6,8,79407,38463,42798,94150,76554,56777,71407,51788,4624,41079,13549,75920,18979,6078,26178,18316}'::bigint[]))  
Planning time: 0.210 ms 
Execution time: 1.193 ms 

занимает 1,2 мсек (было 4,7 мсек при первом запуске)

select tips.id
from tips
where tips.users_id in (select users_id_to
                        from follows
                        where users_id_from = 1
                       )


Merge Semi Join  (cost=2.29..22.07 rows=573 width=8) (actual time=0.540..10632.242 rows=597 loops=1)    
  Merge Cond: (tips.users_id = follows.users_id_to) 
  Buffers: shared hit=1095506 read=1264002  
  ->  Index Scan using tips_idx_users_id01 on tips  (cost=0.43..205139.43 rows=2500000 width=16) (actual time=0.021..10180.667 rows=2353909 loops=1)    
        Buffers: shared hit=1095505 read=1264002    
  ->  Sort  (cost=1.77..1.82 rows=22 width=8) (actual time=0.051..0.084 rows=22 loops=1)    
        Sort Key: follows.users_id_to   
        Sort Method: quicksort  Memory: 26kB    
        Buffers: shared hit=1   
        ->  Seq Scan on follows  (cost=0.00..1.27 rows=22 width=8) (actual time=0.012..0.019 rows=22 loops=1)   
              Filter: (users_id_from = 1)   
              Buffers: shared hit=1 
Planning time: 0.954 ms 
Execution time: 10632.376 ms

занимает 10433 мсек
Определения:

CREATE TABLE public.follows (
  id             bigserial NOT NULL,
  users_id_from  bigint NOT NULL DEFAULT 0,
  users_id_to    bigint NOT NULL DEFAULT 0,
  has_accepted   boolean NOT NULL DEFAULT true,
  created_on     timestamp without time zone NOT NULL DEFAULT CURRENT_TIMESTAMP,
  CONSTRAINT followings_pkey
    PRIMARY KEY (id)
)

CREATE TABLE public.tips (
  id             bigserial NOT NULL,
  users_id       bigint NOT NULL,
  temp_id      bigint NOT NULL,
  first_seen    numeric(12,2) NOT NULL DEFAULT 0,
  created_on     timestamp without time zone NOT NULL DEFAULT CURRENT_TIMESTAMP,
  expire_on_gmt  timestamp without time zone NOT NULL DEFAULT CURRENT_TIMESTAMP,
  ip_from        inet NOT NULL DEFAULT '0.0.0.0'::inet,
  "type"         smallint NOT NULL DEFAULT 0,
  growth         numeric(8,1) NOT NULL DEFAULT 0.0,
  seen          boolean DEFAULT false,

  CONSTRAINT tips_pkey
    PRIMARY KEY (id)
)

CREATE INDEX tips_idx_users_id01
  ON public.tips
  (users_id);

Я действительно не понимаю, почему эта низкая производительность, кажется, что сервер делает JOIN под капотом ...
Любая помощь приветствуется.

Спасибо
Перес

РЕДАКТИРОВАТЬ - 2018.10.9
Несмотря на принятый ответ, который немедленно решает проблему, благодаря более глубокому расследованию Павла Стеуле (см. Сообщения ниже),реальная проблема заключалась в неверной статистике таблицы , следующей за .ВАКУУМНЫЙ АНАЛИЗ решит проблему, оба запроса теперь выполняются быстро.

Ответы [ 2 ]

0 голосов
/ 07 октября 2018

Я бы порекомендовал написать запрос следующим образом:

select t.id
from tips t
where exists (select 1
              from follows f
              where f.users_id_from = 1 and f.users_id_to = t.users_id
             );

И создать индекс для follow(users_id_to, users_id_from) - два столбца в этом порядке.

Относительно того, почему Postgres выбирает это выполнениеплан.Постгрес считает, что это лучший.Иногда оптимизаторы делают ошибки.Возможно, статистика не обновлена ​​в таблице.

РЕДАКТИРОВАТЬ:

Хммм.Интересно, будет ли любая из этих версий побуждать Postgres использовать индекс для tips(id):

with f as (
      select users_id_to
      from follows
      where users_id_from = 1
     )
select t.id
from tips t
where t.users_id in (select f.users_id_to from f);

Это дает Postgres возможность (поощряет?) Материализовать подзапрос и затем использовать индекс.

Второй будет простой join:

select t.id
from tips t join
     follows f
     on f.users_id_to = t.id
where f.users_id_from = 1
0 голосов
/ 07 октября 2018

Я пробую контрольный пример, и у меня совершенно другой план:

postgres=# explain analyze select * from foo where a in (select a from boo where b = 22);
+------------------------------------------------------------------------------------------------------------------------------+
|                                                          QUERY PLAN                                                          |
+------------------------------------------------------------------------------------------------------------------------------+
| Nested Loop  (cost=16.19..7066.65 rows=2101 width=8) (actual time=0.444..11.667 rows=2713 loops=1)                           |
|   ->  HashAggregate  (cost=9.43..9.50 rows=7 width=4) (actual time=0.094..0.111 rows=9 loops=1)                              |
|         Group Key: boo.a                                                                                                     |
|         ->  Bitmap Heap Scan on boo  (cost=4.33..9.42 rows=7 width=4) (actual time=0.048..0.071 rows=9 loops=1)              |
|               Recheck Cond: (b = 22)                                                                                         |
|               Heap Blocks: exact=5                                                                                           |
|               ->  Bitmap Index Scan on boo_b_idx  (cost=0.00..4.33 rows=7 width=0) (actual time=0.030..0.030 rows=9 loops=1) |
|                     Index Cond: (b = 22)                                                                                     |
|   ->  Bitmap Heap Scan on foo  (cost=6.75..1005.16 rows=300 width=8) (actual time=0.256..1.143 rows=301 loops=9)             |
|         Recheck Cond: (a = boo.a)                                                                                            |
|         Heap Blocks: exact=2678                                                                                              |
|         ->  Bitmap Index Scan on foo_a_idx  (cost=0.00..6.68 rows=300 width=0) (actual time=0.145..0.145 rows=301 loops=9)   |
|               Index Cond: (a = boo.a)                                                                                        |
| Planning time: 0.971 ms                                                                                                      |
| Execution time: 12.105 ms                                          ဠ                                                         |
+------------------------------------------------------------------------------------------------------------------------------+
(15 rows)

И хотя я наказываю некоторые методы, у меня значительно лучшие планы -

postgres=# explain analyze select * from foo where a in (select a from boo where b = 22);
+----------------------------------------------------------------------------------------------------------------------------+
|                                                         QUERY PLAN                                                         |
+----------------------------------------------------------------------------------------------------------------------------+
| Nested Loop  (cost=18.03..7894.11 rows=2101 width=8) (actual time=0.433..9.809 rows=2713 loops=1)                          |
|   ->  Unique  (cost=17.60..17.63 rows=7 width=4) (actual time=0.384..0.407 rows=9 loops=1)                                 |
|         ->  Sort  (cost=17.60..17.62 rows=7 width=4) (actual time=0.383..0.388 rows=9 loops=1)                             |
|               Sort Key: boo.a                                                                                              |
|               Sort Method: quicksort  Memory: 25kB                                                                         |
|               ->  Seq Scan on boo  (cost=0.00..17.50 rows=7 width=4) (actual time=0.047..0.358 rows=9 loops=1)             |
|                     Filter: (b = 22)                                                                                       |
|                     Rows Removed by Filter: 991                                                                            |
|   ->  Index Scan using foo_a_idx on foo  (cost=0.43..1122.21 rows=300 width=8) (actual time=0.023..0.874 rows=301 loops=9) |
|         Index Cond: (a = boo.a)                                                                                            |
| Planning time: 0.957 ms                                                                                                    |
| Execution time: 10.399 ms                                                                                                  |
+----------------------------------------------------------------------------------------------------------------------------+
(12 rows)

Протестировано на PostgreSQL 10.5

После нескольких игр я получил:

+------------------------------------------------------------------------------------------------------------------------------------------------------+
|                                                                      QUERY PLAN                                                                      |
+------------------------------------------------------------------------------------------------------------------------------------------------------+
| Gather  (cost=1018.03..117733.71 rows=2101 width=8) (actual time=113.420..914.035 rows=2713 loops=1)                                                 |
|   Workers Planned: 2                                                                                                                                 |
|   Workers Launched: 2                                                                                                                                |
|   ->  Merge Semi Join  (cost=18.03..116523.61 rows=875 width=8) (actual time=150.675..904.224 rows=904 loops=3)                                      |
|         Merge Cond: (foo.a = boo.a)                                                                                                                  |
|         ->  Parallel Index Scan using foo_a_idx on foo  (cost=0.43..113510.99 rows=1250000 width=8) (actual time=0.136..800.463 rows=919564 loops=3) |
|         ->  Sort  (cost=17.60..17.62 rows=7 width=4) (actual time=0.347..0.357 rows=9 loops=3)                                                       |
|               Sort Key: boo.a                                                                                                                        |
|               Sort Method: quicksort  Memory: 25kB                                                                                                   |
|               ->  Seq Scan on boo  (cost=0.00..17.50 rows=7 width=4) (actual time=0.059..0.286 rows=9 loops=3)                                       |
|                     Filter: (b = 22)                                                                                                                 |
|                     Rows Removed by Filter: 991                                                                                                      |
| Planning time: 0.903 ms                                                                                                                              |
| Execution time: 914.283 ms                                                                                                                           |
+------------------------------------------------------------------------------------------------------------------------------------------------------+
(14 rows)

Странно, что у вас нет активного паралелизма (возможно, из-за низкой стоимости, но оценка выглядит хорошо).И хотя я был довольно уродлив на оптимизаторе, я получил запрос максимум 1сек.

Можете ли вы запустить VACUUM FULL в полной базе данных?Не является ли какое-либо другое действие в вашем IO?

Последующие действия - проблема была связана с отсутствующей или устаревшей статистикой в ​​следующей таблице.Это имеет драматический эффект, потому что объединение слиянием имеет некоторую оптимизацию, основанную на сравнении максимальных значений из обеих таблиц.Когда одно значение значительно ниже, тогда ожидается меньшее чтение и раннее окончание.Это является причиной странной низкой стоимости объединения слиянием.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...