Есть ли способ ускорить этот многостоловый запрос с помощью индексов? - PullRequest
0 голосов
/ 17 мая 2019

Я пытаюсь получить все теги, которые принадлежат всем разговорам пользователя (у пользователя много разговоров через ConversationUserPair объединение) - но запрос занимает в среднем 2000 мс.

SELECT "tags"."tag_text_downcased"
FROM "tags"
INNER JOIN "conversations" ON "tags"."conversation_id" = "conversations"."id"
INNER JOIN "conversation_user_pairs" ON "conversations"."id" = "conversation_user_pairs"."conversation_id"
WHERE "conversation_user_pairs"."user_id" = ?
AND "conversation_user_pairs"."conversation_status" = ?
AND ("tags"."user_id" = ?);

Когда я запускаю EXPLAIN ANALYZE в консоли psql, я получаю следующий ответ:

EXPLAIN ANALYZE
SELECT "tags"."tag_text_downcased" FROM "tags" INNER JOIN "conversations" ON "tags"."conversation_id" = "conversations"."id" INNER JOIN "conversation_user_pairs" ON "conversations"."id" = "conversation_user_pairs"."conversation_id" WHERE "conversation_user_pairs"."user_id" = '459' AND "conversation_user_pairs"."conversation_status" = 'active' AND ("tags"."user_id" = '459');

Nested Loop  (cost=462.87..486.65 rows=1 width=11) (actual time=0.457..1.886 rows=40 loops=1)
   Join Filter: (tags.conversation_id = conversations.id)
   ->  Merge Join  (cost=462.78..482.97 rows=1 width=19) (actual time=0.401..1.334 rows=40 loops=1)
         Merge Cond: (tags.conversation_id = conversation_user_pairs.conversation_id)
         ->  Sort  (cost=462.70..462.83 rows=259 width=15) (actual time=0.332..0.337 rows=40 loops=1)
               Sort Key: tags.conversation_id
               Sort Method: quicksort  Memory: 27kB
               ->  Bitmap Heap Scan on tags  (cost=4.49..460.62 rows=259 width=15) (actual time=0.152..0.295 rows=40 loops=1)
                     Recheck Cond: (user_id = 459)
                     Heap Blocks: exact=23
                     ->  Bitmap Index Scan on index_tags_on_user_id_and_conversation_id  (cost=0.00..4.47 rows=259 width=0) (actual time=0.105..0.105 rows=40 loops=1)
                           Index Cond: (user_id = 459)
         ->  Index Only Scan using by_user_and_conversation_and_status on conversation_user_pairs  (cost=0.08..20.02 rows=522 width=4) (actual time=0.066..0.956 rows=390 loops=1)
               Index Cond: ((user_id = 459) AND (conversation_status = 'active'::text))
               Heap Fetches: 134
   ->  Index Only Scan using index_conversations_on_id on conversations  (cost=0.08..3.68 rows=1 width=4) (actual time=0.013..0.013 rows=1 loops=40)
         Index Cond: (id = conversation_user_pairs.conversation_id)
         Heap Fetches: 40

Я думаю, что у меня есть соответствующие индексы для трех рассматриваемых таблиц.У меня есть:

add_index "tags", ["conversation_id", "user_id", "tag_text_downcased"], name: "find_tag_text_downcased_tags"
add_index "tags", ["conversation_id", "user_id"], name: "index_conversation_first_tags"
add_index "tags", ["user_id", "conversation_id"], name: "index_tags_on_user_id_and_conversation_id"

add_index "conversation_user_pairs", ["user_id", "conversation_id", "conversation_status"], name: "by_user_and_conversation_and_status"

add_index "conversations", ["id"], name: "index_conversations_on_id"

Не нужно ли здесь что-то сделать, чтобы ускорить запрос, так как похоже, что он использует индекс из каждой таблицы?Или есть способ иметь индекс для нескольких таблиц?

1 Ответ

0 голосов
/ 17 мая 2019

Я заполняю образованными догадками, где информация отсутствует ...

Запрос

Отображаемый вами запрос не идеален для вашей заявленной цели:

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

И под словом «все» вы подразумеваете «любое», которое я предполагаю.

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

При наличии запроса запрос может возвращать одни и те же теги несколько раз.Предполагая, что вам нужны уникальные теги, достаточно утверждать, что любые совпадающие строки в conversation_user_pairs существуют .Полу-соединение EXISTS обычно является лучшим способом сделать это:

SELECT t.tag_text_downcased
FROM   tags t
WHERE  t.user_id = 459  -- assuming it's a numeric data type
AND    EXISTS (
   SELECT
   FROM   conversation_user_pairs cu
   WHERE  cu.user_id         = t.user_id
   AND    cu.conversation_id = t.conversation_id
   AND    cu.conversation_status = 'active'
   );

Индексы

Ваш индекс find_tag_text_downcased_tags на tags идеален.
И by_user_and_conversation_and_status тоже хорошо для этого.Если многие строки не являются «активными», в то время как вас больше всего интересуют активные, частичный индекс может быть даже лучше:

CREATE INDEX ON conversation_user_pairs (user_id, conversation_id)
WHERE conversation_status = 'active';

Другие индексы здесь вам не нужны.И так как у вас есть эти два:

add_index "tags", ["conversation_id", "user_id", "tag_text_downcased"], name: "find_tag_text_downcased_tags"
add_index "tags", ["user_id", "conversation_id"], name: "index_tags_on_user_id_and_conversation_id"

... обычно это тоже не полезно:

add_index "tags", ["conversation_id", "user_id"], name: "index_conversation_first_tags"

Вы, вероятно, можете отбросить его.См .:

В стороне: если conversation_status имеет только «активный»и «мертвый» или подобный, сделайте это boolean столбцом.Меньше и дешевле, чем text.

...