SQL Считайте на большом столе, используя несколько соединений - mysql speed tip? - PullRequest
1 голос
/ 21 февраля 2020

Можно ли как-нибудь улучшить этот запрос, чтобы повысить производительность?

SELECT count(stories.id), count(distinct table_1.table_2_id), count(distinct table_1.id)
FROM stories
    INNER JOIN table_1 ON (table_1.id = stories.table_1_id)
    INNER JOIN table_2 ON (table_2.id = table_1.table_2_id)
    INNER JOIN table_3 ON (table_3.id = table_2.table_3_id)
    INNER JOIN table_4 ON (table_4.id = table_3.table_4_id)
    INNER JOIN table_5 ON (table_5.id = table_4.table_5_id)
WHERE stories.id in (select s2.id
                    from stories s2
                    where s2.published_at between '2015-01-01' and '2020-02-21'
                    and s2.deleted = false
                    )

Мне нужна эта информация (количество) для общей суммы панели инструментов.

Все задействованные таблицы равны Inno DB, у меня есть индексы для всех соединяемых идентификаторов. Моя таблица историй в настоящее время насчитывает более 15 миллионов записей и продолжает расти.

Каков наилучший подход с точки зрения масштабируемости для решения такой ситуации?

Ответы [ 2 ]

0 голосов
/ 23 февраля 2020
SELECT  count(*),
        count(distinct table_1.table_2_id),
        count(distinct table_1.id)
    FROM stories AS s
    JOIN table_1 ON (table_1.id = stories.table_1_id)
    JOIN table_2 ON (table_2.id = table_1.table_2_id)
    WHERE s.published_at >= '2015-01-01'
      AND s.published_at  < '2015-01-01' + INTERVAL 3 WEEK
      AND s.deleted = FALSE
      AND EXISTS( SELECT 1
                     FROM table_3 ON (table_3.id = table_2.table_3_id)
                     JOIN table_4 ON (table_4.id = table_3.table_4_id)
                     JOIN table_5 ON (table_5.id = table_4.table_5_id) );

Примечания:

  • Используйте COUNT(*) вместо COUNT(x), если только вам не нужно проверять x на NULLness.
  • EXISTS быстрее JOINing потому что он останавливается, как только одна строка найдена.
  • Нет необходимости делать полный JOIN; он аннулируется DISTINCT.
  • stories нуждается INDEX(deleted, published_at, table_2_id, id) - в этом порядке. (Это делает его «покрывающим».)

Пожалуйста, укажите EXPLAIN SELECT ....

0 голосов
/ 21 февраля 2020

Конечно, вам нужно множество соответствующих индексов.

  1. Один индекс, который вам нужен, который легко упустить, это

    CREATE INDEX stories_deleted_published_at ON stories(deleted, published_at);
    
  2. Если бы не фильтрация только некоторых записей истории, вы могли бы разделить счет story.id на

    SELECT count(*) from stories;
    

    Базы данных часто ведут подсчет количества записей в таблице и могут оптимизировать это до очень быстрый ответ. Однако в этом случае это не относится.

  3. Я предполагаю, что каждое поле id является первичным ключом. Если вы уже запретили сиротам в своей базе данных, то последнее соединение становится проверкой, если table_4.table_5_id равно NULL. Если это так, вы можете попробовать:

    SELECT count(stories.id), count(distinct table_1.table_2_id), count(distinct table_1.id)
    FROM stories
        INNER JOIN table_1 ON (table_1.id = stories.table_1_id)
        INNER JOIN table_2 ON (table_2.id = table_1.table_2_id)
        INNER JOIN table_3 ON (table_3.id = table_2.table_3_id)
        INNER JOIN table_4 ON (table_4.id = table_3.table_4_id)
                              AND table_4.table_5_id IS NOT NULL
    WHERE stories.id in (select s2.id
                    from stories s2
                    where s2.published_at between '2015-01-01' and '2020-02-21'
                    and s2.deleted = false
                    )
    
  4. Вы сказали, что это было медленнее, когда подзапрос заменен на stories.published_at between '2015-01-01' and '2020-02-21' and stories.deleted = false. Просто, чтобы попробовать это, вы можете иметь это в предложении соединения. Это может быть подсказкой оптимизатору отфильтровать записи при выполнении объединения вместо фильтрации результатов после выполнения объединения.

    SELECT count(stories.id), count(distinct table_1.table_2_id), count(distinct table_1.id)
    FROM stories
        INNER JOIN table_1 ON (table_1.id = stories.table_1_id)
                              AND stories.published_at between '2015-01-01' and '2020-02-21'
                              AND stories.deleted = false
        INNER JOIN table_2 ON (table_2.id = table_1.table_2_id)
        INNER JOIN table_3 ON (table_3.id = table_2.table_3_id)
        INNER JOIN table_4 ON (table_4.id = table_3.table_4_id)
                              AND table_4.table_5_id IS NOT NULL
    
...