2015-04-11 5 views
1

В настоящее время я работаю над проектом, который будет использовать базу данных PostgreSQL (в конечном счете) около 200 миллионов строк, в которой две таблицы имеют интересные отношения. Таблица A содержит элементы, среди прочего, поля A.a, A.b и A.c, тогда как таблица B содержит B.a, B.b, B.c и значение B.v для каждого из них. Цель здесь состоит в том, чтобы иметь возможность присоединиться к таблицам A и B, чтобы я мог получить значение B.v для каждой записи в A. В настоящее время у меня установлена ​​B с B.a, B.b и B.c как индекс с несколькими столбцами с уникальным свойством. Это лучший способ представить эти отношения (имея в виду, что таблица A может содержать миллионы строк, а B будет содержать только 10 000). Если есть лучший способ сохранить это, я хотел бы узнать.PostgreSQL slow Предложение WHERE в разреженном соединении

Для того, чтобы усложнить, я также требую некоторой информации из третьей таблицы C, который я также присоединиться к А и В.

Для иллюстрации структуры таблиц:

     Table "A" 
    Column | Type | Modifiers | Storage | Stats target | Description 
--------------+----------+-----------+---------+--------------+------------- 
id   | bigint |   | plain |    | 
a   | boolean | not null | plain |    | 
b   | integer |   | plain |    | 
c   | smallint |   | plain |    | 
user_id  | bigint |   | plain |    | 
Indexes: 
    PRIMARY KEY, btree (id) 
    UNIQUE CONSTRAINT, btree (id) 
    btree (user_id) 
    btree (a) 
    btree (b) 
    btree (c) 

               Table "B" 
    Column | Type |       Modifiers       | Storage | Stats target | Description 
--------------+----------+------------------------------------------------------------+---------+--------------+------------- 
id   | integer | not null default nextval('A_id_seq'::regclass)    | plain |    | 
a   | boolean | not null             | plain |    | 
b   | integer | not null             | plain |    | 
c   | smallint | not null             | plain |    | 
v   | bigint | not null             | plain |    | 
Indexes: 
    PRIMARY KEY, btree (id) 
    UNIQUE CONSTRAINT, btree (a, b, c) 
    btree (c) 
    btree (b) 
    btree (b, c, a) 
    btree (v) 
Foreign-key constraints: 
    ... 

           Table "C" 
    Column |   Type   | Modifiers | Storage | Stats target | Description 
--------------+--------------------------+-----------+----------+--------------+------------- 
user_id  | bigint     | not null | plain |    | 
p   | integer     |   | plain |    | 
Indexes: 
    PRIMARY KEY, btree (user_id) 
    UNIQUE CONSTRAINT, btree (user_id) 
    btree (p) 

Цель состоит в том, то чтобы найти все строки в таблице а, где соответствующее значение в B больше, чем 1000, а значение в C больше, чем 100. в настоящее время мой запрос для этого заключается в следующем:

SELECT * FROM A 
    INNER JOIN B ON 
     A.a = B.a AND 
     A.b = B.b AND 
     A.c = B.c 
    LEFT JOIN C ON 
     A.user_id = C.user_id 
    WHERE C.p < 100 AND B.v > 1000 
    LIMIT 100; 

Это, однако, очень медленно, за несколько секунд до конца. В частности, удаление ограничения B.v> 1000 ускоряет запрос с примерно 6 секунд до 20 миллисекунд. Я подозреваю, что это потому, что записи с значением B.v более 1000 очень редки (около 20 000 в миллионах). Затем я использовал EXPLAIN ANALYZE положение о запросе, чтобы произвести следующее:

Limit (cost=0.00..1133.08 rows=100 width=215) (actual time=18.516..6571.243 rows=100 loops=1) 
    -> Nested Loop (cost=0.00..1940860.96 rows=171291 width=215) (actual time=18.513..6571.113 rows=100 loops=1) 
     -> Nested Loop (cost=0.00..1232018.65 rows=1171013 width=64) (actual time=9.293..6326.114 rows=3303 loops=1) 
       -> Seq Scan on A (cost=0.00..81726.86 rows=3888586 width=36) (actual time=0.018..857.948 rows=452207 loops=1) 
       -> Index Scan using [...] on B (cost=0.00..0.28 rows=1 width=28) (actual time=0.010..0.010 rows=0 loops=452207) 
        Index Cond: (a = a AND b = b AND c = c) 
        Filter: (v > 1000) 
     -> Index Scan using user_id_pk on C (cost=0.00..0.59 rows=1 width=151) (actual time=0.072..0.072 rows=0 loops=3303) 
       Index Cond: (user_id = A.user_id) 
       Filter: (p < 80) 
Total runtime: 6571.550 ms 

Это кажется странным, что это заняло бы гораздо дольше, чтобы добавить одно ограничение на запрос только потому, что количество матчей в том, что Ограничение мало (в качестве побочного элемента, уменьшая его с 1000 до 10, что дает гораздо больше результатов, также обеспечивает огромное ускорение).

Итак, мой вопрос заключается в следующем: может ли сервер базы данных не использовать индекс, который я применил к B.v в этом запросе? Кроме того, можно ли каким-либо образом ускорить этот запрос?

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

Edit: план Исполнение без B.v> 1000 пункта:

Limit (cost=0.00..13278.28 rows=100 width=215) (actual time=76.185..237.820 rows=100 loops=1) 
    -> Nested Loop (cost=0.00..1383862.57 rows=10422 width=215) (actual time=76.183..237.739 rows=100 loops=1) 
     -> Nested Loop (cost=0.00..1322702.97 rows=71251 width=64) (actual time=18.270..121.272 rows=840 loops=1) 
       -> Seq Scan on A (cost=0.00..85715.82 rows=4078382 width=36) (actual time=0.037..2.520 rows=1377 loops=1) 
       -> Index Scan using [...] on B (cost=0.00..0.28 rows=1 width=28) (actual time=0.082..0.083 rows=1 loops=1377) 
        Index Cond: (a = a AND b = b AND c = c) 
     -> Index Scan using user_id_pk on C (cost=0.00..0.85 rows=1 width=151) (actual time=0.136..0.136 rows=0 loops=840) 
       Index Cond: (user_id = A.user_id) 
       Filter: (C.p < 80) 
Total runtime: 238.076 ms 
+1

Почему ЛЕВАЯ ВСТУПЛЕНИЕ, когда условие WHERE делает его ВХОДНЫМ ОБЪЕДИНЕНИЕМ? C не является необязательным, LEFT JOIN не имеет смысла. Или переместите условие WHERE на Cp в условие LEFT JOIN, когда результаты C являются необязательными: ON A.user_id = C.user_id AND Cp <100. –

+0

1) 'left JOIN + where ...' на самом деле является простым соединением (что Фрэнк говорит выше) 2) у вас нет статистики (расчетные и фактические затраты отличаются); 'ВАКУУМНЫЙ АНАЛИЗ' на каждом столе. 3) не называйте свои столбцы {a, b, c} – wildplasser

ответ

0

Если предикат B.v очень избирательно, а затем поместить индекс по этому столбцу.

Я подозреваю, что причиной медленности с этим предикатом является то, что запрос не может выполняться только на основе доступа к уникальному индексу на B - вместо этого каждая строка в таблице должна быть доступна для проверки значения v также.

+0

Большое спасибо за ваш ответ. Однако, насколько я вижу, на B.v. уже есть индекс B-дерева. С этим индексом что-то не так, или PostgreSQL не использует его? – StephenSwat

+1

Вы бы лучше опубликовать оба объяснения планов –

+0

Я добавил план выполнения без предложения B.v> 1000. – StephenSwat

Смежные вопросы