Оптимизация запроса

Discussion in 'PHP' started by 5w17ch3r, 2 Feb 2016.

  1. 5w17ch3r

    5w17ch3r Member

    Joined:
    1 Dec 2008
    Messages:
    79
    Likes Received:
    11
    Reputations:
    0
    Есть база MySQL, таблица на InnoDB с парой-тройкой миллионов записей.
    Структура таблицы:
    id | server_id | bytes | date

    Из данных таблицы формируется статистика за промежутки времени следующим запросом:
    Code:
    SELECT
                server.name as name,
                m.count as minute,
                h.count as hour,
                d.count as day,
                w.count as week,
                30d.count as thirty,
                overall.count as total,
                overall.date as updated
            FROM
                server
            LEFT JOIN (SELECT SUM(bytes) as count, server_id FROM traffic t WHERE date >= DATE_SUB(NOW(), INTERVAL 1 MINUTE) GROUP BY server_id) as m
                ON m.server_id = server.id
            LEFT JOIN (SELECT SUM(bytes) as count, server_id FROM traffic t WHERE date >= DATE_SUB(NOW(), INTERVAL 1 HOUR) GROUP BY server_id) h
                ON h.server_id = server.id
            LEFT JOIN (SELECT SUM(bytes) as count, server_id FROM traffic t WHERE date >= DATE_SUB(NOW(), INTERVAL 1 DAY) GROUP BY server_id) d
                ON d.server_id = server.id
            LEFT JOIN (SELECT SUM(bytes) as count, server_id FROM traffic t WHERE date >= DATE_SUB(NOW(), INTERVAL 1 WEEK) GROUP BY server_id) w
                ON w.server_id = server.id
            LEFT JOIN (SELECT SUM(bytes) as count, server_id, MAX(date) as date FROM traffic t WHERE date >= DATE_SUB(NOW(), INTERVAL 30 DAY) GROUP BY server_id) 30d
                ON 30d.server_id = server.id
            LEFT JOIN (SELECT SUM(bytes) as count, , MAX(date) as date, server_id FROM traffic t GROUP BY server_id) overall
                ON overall.server_id = server.id
            ORDER BY name
    
    Видно, что запрос получается нагруженным из-за 6 сканирований одной таблицы.
    Вопрос: каким образом можно реализовать получение этих данных за 1-2 прохода по таблице traffic?
     
  2. lastbyte

    lastbyte New Member

    Joined:
    11 Feb 2016
    Messages:
    2
    Likes Received:
    0
    Reputations:
    1
    Первым делом стоит запустить explain http://dev.mysql.com/doc/refman/5.7/en/explain-output.html . Или запустить профилирование запроса https://habrahabr.ru/post/70435/ .

    Есть вариант, правда не совсем в него верю, но может помочь. Смысл в то что mysql не кэширует запросы с использованием NOW(), все left join'ы по факту работают с записями у которых where меньше определенной даты. Если заранее посчитать интервал DATE_SUB(NOW(), INTERVAL 30 DAY), например на PHP, то можно получить общий запрос (SELECT bytes, server_id, date FROM traffic t WHRE date >= $interval), и этот запрос уже попадет в кэш mysql.
    Далее на каждый LEFT JOIN вместо новой выборки, делаем выборку из кэшированного запроса:

    Code:
    (LEFT JOIN SELECT SUM(bytes) as count, server_id FROM (SELECT bytes, server_id, date FROM traffic t WHRE date >= $interval) WHERE date >=DATE_SUB(NOW(), INTERVAL 1 DAY) GROUP BY server_id) d
    ON d.server_id = server_.id
    (LEFT JOIN SELECT SUM(bytes) as count, server_id FROM (SELECT bytes, server_id, date FROM traffic t WHRE date >= $interval) WHERE date >=DATE_SUB(NOW(), INTERVAL 1 HOUR) GROUP BY server_id) h
    ON h.server_id = server_.id
    .....
    
    Правда, повторюсь, это может только замедлить работу.