2015-05-21 5 views
5

У меня есть запрос SQL, который занимает 100% моего процессора VM во время работы. Я хочу знать, как оптимизировать его:Оптимизация запроса SQL

SELECT g.name AS hostgroup 
    , h.name AS hostname 
    , a.host_id 
    , s.display_name AS servicename 
    , a.service_id 
    , a.entry_time AS ack_time 
    , ( SELECT ctime 
      FROM logs 
      WHERE logs.host_id = a.host_id 
      AND logs.service_id = a.service_id 
      AND logs.ctime < a.entry_time 
      AND logs.status IN (1, 2, 3) 
      AND logs.type = 1 
      ORDER BY logs.log_id DESC 
      LIMIT 1) AS start_time 
    , ar.acl_res_name AS timeperiod 
    , a.state AS state 
    , a.author 
    , a.acknowledgement_id AS ack_id 
FROM centstorage.acknowledgements a 
LEFT JOIN centstorage.hosts h ON a.host_id = h.host_id 
LEFT JOIN centstorage.services s ON a.service_id = s.service_id 
LEFT JOIN centstorage.hosts_hostgroups p ON a.host_id = p.host_id 
LEFT JOIN centstorage.hostgroups g ON g.hostgroup_id = p.hostgroup_id 
LEFT JOIN centreon.hostgroup_relation hg ON a.host_id = hg.host_host_id 
LEFT JOIN centreon.acl_resources_hg_relations hh ON hg.hostgroup_hg_id = hh.hg_hg_id 
LEFT JOIN centreon.acl_resources ar ON hh.acl_res_id = ar.acl_res_id 
WHERE ar.acl_res_name != 'All Resources' 
AND YEAR(FROM_UNIXTIME(a.entry_time)) = YEAR(CURDATE()) 
AND MONTH(FROM_UNIXTIME(a.entry_time)) = MONTH(CURDATE()) 
AND a.service_id is not null 
ORDER BY a.acknowledgement_id ASC 

Проблема заключается в этой части:

(SELECT ctime FROM logs 
WHERE logs.host_id = a.host_id 
    AND logs.service_id = a.service_id 
    AND logs.ctime < a.entry_time 
    AND logs.status IN (1, 2, 3) 
    AND logs.type = 1 
ORDER BY logs.log_id DESC 
LIMIT 1) AS start_time 

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

Там находится ОБЪЯСНИТЬ РАСПРОСТРАНЕНИЕ запроса: Here !

Кажется, что он будет рассмотрен только 2 строки журналов таблицы, так почему это занимает так много времени? (В журналах таблицы есть 560000 строк).

Здесь все индексы этих таблиц:

centstorage.acknowledgements:

enter image description here centstorage.hosts:

enter image description here centstorage.services:

enter image description here centstorage.hosts_hostgroups:

enter image description here centstorage.hostgroups:

enter image description here centreon.hostgroup_relation:

enter image description here centreon.acl_resources_hg_relations:

enter image description here centreon.acl_resources:

enter image description here

+0

Какой продукт dbms? Определения таблиц и индексов и т. Д. – jarlh

+2

Прежде всего проверьте план выполнения и убедитесь, что вам не хватает индекса. – Galma88

+0

Я использую MySQL. –

ответ

0

Для SQL Server есть возможность определить максимальную степень параллелизма вашего запроса с помощью MAXDOP

Например, вы можете определить в конце запроса

option (maxdop 2) 

Я уверен, что есть более эквивалент в MySql.

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

+1

нет такой вещи для mysql, не обойтись без сторонних надстроек –

0
  1. Создайте временную таблицу, где условие для подтверждения, схема будет иметь столбец, требуемую в конечном результате и используется в JOIN со всеми 7 таблицами

    CREATE TEMPORARY TABLE __tempacknowledgements AS SELECT g.name AS hostgroup 
        , '' AS hostname 
        , a.host_id 
        , s.display_name AS servicename 
        , a.service_id 
        , a.entry_time AS ack_time 
        , '' AS AS start_time 
        , '' AS timeperiod 
        , a.state AS state 
        , a.author 
        , a.acknowledgement_id AS ack_id 
    FROM centstorage.acknowledgements a 
    WHERE YEAR(FROM_UNIXTIME(a.entry_time)) = YEAR(CURDATE()) 
    AND MONTH(FROM_UNIXTIME(a.entry_time)) = MONTH(CURDATE()) 
    AND a.service_id IS NOT NULL 
    ORDER BY a.acknowledgement_id ASC; 
    

Или создать с помощью правильного определения столбца

  1. Обновить поля из всех таблиц, имеющих левое соединение, вы можете использовать Inner Join в обновлении. Вы должны написать 7 разных операторов обновления. Ниже приводятся 2 примера.

    UPDATE __tempacknowledgements a JOIN centstorage.hosts h USING(host_id) 
    SET a.name=h.name; 
    
    UPDATE __tempacknowledgements s JOIN centstorage.services h USING(service_id) 
    SET a.acl_res_name=s.acl_res_name; 
    
  2. подобный способ обновления CTime из бревен с помощью регистрации с журналами, это восьмые оператор обновления.

  3. выбрать из таблицы темп.
  4. падение температуры Таблица

зр могут быть написаны для этого.

+0

Кажется, это хороший способ помочь мне, но я все еще новичок в SQL-запросах. поэтому я не получаю все это –

+0

Добавлен пример запроса, может помочь вам получить представление о предлагаемом решении. – Anil

0

Включите LEFT JOIN в JOIN, если у вас нет реальной потребности в LEFT.

AND YEAR(FROM_UNIXTIME(a.entry_time)) = YEAR(CURDATE()) 
AND MONTH(FROM_UNIXTIME(a.entry_time)) = MONTH(CURDATE()) 
AND a.service_id is not null 

У вас есть несколько строк с a.service_id is not null? Если нет, избавитесь от него.

Как уже упоминалось, сравнение даты не оптимизируется. Вот что нужно использовать вместо:

AND a.entry_time >= CONCAT(LEFT(CURDATE(), 7), '-01') 
AND a.entry_time < CONCAT(LEFT(CURDATE(), 7), '-01') + INTERVAL 1 MONTH 

И добавить один из них (в зависимости от моего выше комментария):

INDEX(entry_time) 
INDEX(service_id, entry_time) 

Коррелированная подзапрос трудно оптимизировать. Этот показатель (на logs) может помочь:

INDEX(type, host_id, service_id, status) 
0

ГДЕ В это время убийца! Вместо logs.status IN (1, 2, 3) использование logs.status = 1 или logs.status = 2 или logs.status = 3

+0

Не могли бы вы немного разобраться? –

0

Я ЧУТЬ переформатирован запрос для моя рекомендация по удобочитаемости и лучшее наблюдение за отношениями между таблицами ... иначе игнорируйте эту часть.

SELECT 
     g.name AS hostgroup, 
     h.name AS hostname, 
     a.host_id, 
     s.display_name AS servicename, 
     a.service_id, 
     a.entry_time AS ack_time, 
     (SELECT 
       ctime 
      FROM 
       logs 
      WHERE 
        logs.host_id = a.host_id 
       AND logs.service_id = a.service_id 
       AND logs.ctime < a.entry_time 
       AND logs.status IN (1, 2, 3) 
       AND logs.type = 1 
      ORDER BY 
       logs.log_id DESC 
      LIMIT 1) AS start_time, 
     ar.acl_res_name AS timeperiod, 
     a.state AS state, 
     a.author, 
     a.acknowledgement_id AS ack_id 
    FROM 
     centstorage.acknowledgements a 
     LEFT JOIN centstorage.hosts h 
      ON a.host_id = h.host_id 
     LEFT JOIN centstorage.services s 
      ON a.service_id = s.service_id 
     LEFT JOIN centstorage.hosts_hostgroups p 
      ON a.host_id = p.host_id 
      LEFT JOIN centstorage.hostgroups g 
       ON p.hostgroup_id = g.hostgroup_id 
     LEFT JOIN centreon.hostgroup_relation hg 
      ON a.host_id = hg.host_host_id 
      LEFT JOIN centreon.acl_resources_hg_relations hh 
       ON hg.hostgroup_hg_id = hh.hg_hg_id 
       LEFT JOIN centreon.acl_resources ar 
        ON hh.acl_res_id = ar.acl_res_id 
    WHERE 
      ar.acl_res_name != 'All Resources' 
     AND YEAR(FROM_UNIXTIME(a.entry_time)) = YEAR(CURDATE()) 
     AND MONTH(FROM_UNIXTIME(a.entry_time)) = MONTH(CURDATE()) 
     AND a.service_id is not null 
    ORDER BY 
     a.acknowledgement_id ASC 

Я бы первым рекомендую начинать с таблицы «Выражение признательности» и есть индекс на минимуме (entry_time, acknowledgement_id). Затем обновите предложение WHERE. Поскольку вы используете функцию для преобразования временной отметки unix в дату и захвата YEAR (и месяца) соответственно, я не считаю, что она использует индекс, поскольку он должен вычислить это для каждой строки. Чтобы повысить это, временная метка unix представляет собой не что иное, как число, представляющее секунды из определенного момента времени. Если вы ищете определенный месяц, то предварительно вычислите начальное и конечное время unix и запустите для этого диапазона. Что-то вроде...

и a.entry_time> = UNIX_TIMESTAMP ('2015-10-01') и a.entry_time < UNIX_TIMESTAMP ('2015-11-01')

Таким образом, он учитывает все в пределах секунд месяц до 11:59:59 31 октября, незадолго до 1 ноября.

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

table    index 
logs    (host_id, service_id, type, status, ctime, log_id) 
acknowledgements (entry_time, acknowledgement_id, host_id, service_id) 
hosts    (host_id, name) 
services   (service_id, display_name) 
hosts_hostgroups (host_id, hostgroup_id) 
hostgroups   (hostgroup_id, name) 
hostgroup_relation (host_host_id, hostgroup_hg_id) 
acl_resources_hg_relations (hh_hg_id, acl_res_id) 
acl_resources ar (acl_res_id, acl_res_name) 

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

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