MySQL: длительная производительность запросов LEFT JOIN - PullRequest
0 голосов
/ 15 февраля 2019

База данных MySQL содержит две таблицы: customer и custmomer_orders

Таблица customer содержит 80 миллионов записей и содержит 80 полей.Некоторые из них меня интересуют:

  1. Id (PK, int (10))
  2. Местоположение (varchar 255, обнуляемый).
  3. Registration_Date (DateTime,обнуляемый).Индексируется.

Таблица customer_orders содержит 40 миллионов записей и содержит только 3 поля:

  1. Id (PK, int (10))
  2. Customer_Id (int (10), FK для таблицы клиентов)
  3. Order_Date (DateTime, nullable)

Когда я запускаю такой запрос, он занимает ~ 800 секунды для выполнения и возвращает 40 миллионов записей:

SELECT o.* 
FROM customer_orders o
LEFT JOIN customer c ON (c.Id = o.Customer_Id) 
WHERE NOT (ISNULL(c.Location)) AND c.Registration_Date < '2018-01-01 00:00:00';

Машина с сервером MySQL имеет 32 ГБ ОЗУ, 28 ГБ выделено для MySQL.Версия MySQL: 5.6.39.

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

Обновление:

Таблица customer_orders не содержит важных данных, которые мы хотели бы сохранить.Это своего рода скопированная таблица с заказами, сделанными за последние 10 дней.Каждый день мы запускаем хранимую процедуру, которая удаляет заказы старше 10 дней в рамках транзакции.

В какой-то момент эта хранимая процедура заканчивалась тайм-аутом из-за неоптимизированного запроса и количестваЗаказы росли с каждым днем.Предыдущий запрос содержал также метод COUNT, который, я полагаю, превысил время ожидания.

Тем не менее, меня удивило, что MySQL может извлечь до 40 м записей с дополнительными условиями.

Ответы [ 4 ]

0 голосов
/ 15 февраля 2019

Я хотел оставить комментарий, но передумал, чтобы идти с ответом.

Поскольку главная проблема - это сам ваш вопрос.

Я не знаю, сколько столбцов у вас customer_orders имеет, но если вы получаете

40 миллионов записей

назад.Я бы сказал, что вы делаете что-то не так.И, вероятно, это не сам запрос медленный, а выборка данных.

Чтобы доказать, что попытайтесь выполнить EXPLAIN по вашему запросу:

EXPLAIN SELECT ...your query here... ;

Затем выполните

EXPLAIN SELECT ...your query here... LIMIT 1;

Попробуйте LIMIT ваши результаты до 1000, например:

SELECT ...your query here... LIMIT 1000;

Когда у вас есть ответы, результаты и статистика для этих запросов, мы можем обсудить ваши следующие шаги.

0 голосов
/ 15 февраля 2019

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

Поскольку вы сказали, что таблица клиентов относительно велика по сравнению с таблицей заказов, это может быть одиниз этих ситуаций.

SELECT o.* 
FROM customer_orders AS o
WHERE o.Customer_Id IN (
     SELECT Id 
     FROM customer 
     WHERE Location IS NOT NULL 
        AND Registration_Date < '2018-01-01 00:00:00'
);
0 голосов
/ 15 февраля 2019

Я думаю, что это нормально.Было бы полезно, если бы вы поделились тем, что explain возвращает для этого запроса.

Для оптимизации запроса, возможно, не стоит начинать с customer_orders, поскольку вы все равно не фильтруете его (поэтому он выполняет полное сканирование таблицы более 40 миллионов записей).Также, как указано в комментариях, LEFT JOIN здесь не требуется.Я бы написал ваш запрос следующим образом:

SELECT o.*
FROM customers c, customer_orders o
WHERE c.id = o.Customer_Id
AND   c.Location IS NOT NULL
AND   c.Registration_Date < '2018-01-01'

Это (в зависимости от того, сколько записей удовлетворяет условию Registration_Date < '2018-01-01') сначала отфильтрует таблицу customers, а затем объединит ее с таблицей customer_orders, которая имеети индексировать по customer_id

Кроме того, возможно, это не связано, но нормально ли для вас, что запрос возвращает 40M записей?Я имею в виду, это как вся таблица customer_orders.Если я прав, это означает, что все заказы от клиента зарегистрированы до '2018-01-01'

0 голосов
/ 15 февраля 2019

Это слишком долго для комментария ...

Первое, на что нужно обратить внимание в вашем запросе, это то, что он на самом деле не выполняет LEFT JOIN, так как в условии WHERE есть условия, которыеобратитесь к таблице LEFT JOIN ed.

Его можно переписать следующим образом:

SELECT o.* 
FROM customer_orders o
INNER JOIN customer c 
    ON c.Id = o.Customer_Id
    AND c.Location is NOT NULL
    AND c.Registration_Date < '2018-01-01 00:00:00';

Явное указание типа соединения лучше для удобства чтения и может помочь MySQL найти лучший путь выполнениядля запроса.

Когда дело доходит до производительности, основной совет заключается в том, что для этого запроса вам потребуется составной индекс для всех трех столбцов, в которых выполняется поиск, в той же последовательности, что и тот, который используется взапрос (как правило, вы хотите поставить более ограничительное условие в начале, так что вы можете настроить это):

ALTER TABLE mytable ADD INDEX (Id, Location, Registration_Date );

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

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