2016-11-08 13 views
2

У меня есть таблица MySQL со следующей структурой:Mysql InnoDB очень медленно по запросу SELECT,

mysql> show create table logs \G; 

Create Table: CREATE TABLE `logs` (
    `id` int(11) NOT NULL AUTO_INCREMENT, 
    `request` text, 
    `response` longtext, 
    `msisdn` varchar(255) DEFAULT NULL, 
    `username` varchar(255) DEFAULT NULL, 
    `shortcode` varchar(255) DEFAULT NULL, 
    `response_code` varchar(255) DEFAULT NULL, 
    `response_description` text, 
    `transaction_name` varchar(250) DEFAULT NULL, 
    `system_owner` varchar(250) DEFAULT NULL, 
    `request_date_time` datetime DEFAULT NULL, 
    `response_date_time` datetime DEFAULT NULL, 
    `comments` text, 
    `user_type` varchar(255) DEFAULT NULL, 
    `channel` varchar(20) DEFAULT 'WEB', 

    /** 

    other columns here.... 

    other 18 columns here, with Type varchar and Text 

    **/ 

    PRIMARY KEY (`id`), 
    KEY `transaction_name` (`transaction_name`) USING BTREE, 
    KEY `msisdn` (`msisdn`) USING BTREE, 
    KEY `username` (`username`) USING BTREE, 
    KEY `request_date_time` (`request_date_time`) USING BTREE, 
    KEY `system_owner` (`system_owner`) USING BTREE, 
    KEY `shortcode` (`shortcode`) USING BTREE, 
    KEY `response_code` (`response_code`) USING BTREE, 
    KEY `channel` (`channel`) USING BTREE, 
    KEY `request_date_time_2` (`request_date_time`), 
    KEY `response_date_time` (`response_date_time`) 
) ENGINE=InnoDB AUTO_INCREMENT=59582405 DEFAULT CHARSET=utf8 

и имеет более 30000000 записей в нем.

mysql> select count(*) from logs; 
+----------+ 
| count(*) | 
+----------+ 
| 38962312 | 
+----------+ 
1 row in set (1 min 17.77 sec) 

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

Мой следующий суб-запрос занимает почти 30 минут, чтобы принести записи в один день:

SELECT 
    COUNT(sub.id) AS count, 
    DATE(sub.REQUEST_DATE_TIME) AS transaction_date, 
    sub.SYSTEM_OWNER, 
    sub.transaction_name, 
    sub.response, 
    MIN(sub.response_time), 
    MAX(sub.response_time), 
    AVG(sub.response_time), 
    sub.channel 
FROM 
    (SELECT 
     id, 
      REQUEST_DATE_TIME, 
      RESPONSE_DATE_TIME, 
      TIMESTAMPDIFF(SECOND, REQUEST_DATE_TIME, RESPONSE_DATE_TIME) AS response_time, 
      SYSTEM_OWNER, 
      transaction_name, 
      (CASE 
       WHEN response_code IN ('0' , '00', 'EIL000') THEN 'Success' 
       ELSE 'Failure' 
      END) AS response, 
      channel 
    FROM 
     logs 
    WHERE 
     response_code != '' 
      AND DATE(REQUEST_DATE_TIME) BETWEEN '2016-10-26 00:00:00' AND '2016-10-27 00:00:00' 
      AND SYSTEM_OWNER != '') sub 
GROUP BY DATE(sub.REQUEST_DATE_TIME) , sub.channel , sub.SYSTEM_OWNER , sub.transaction_name , sub.response 
ORDER BY DATE(sub.REQUEST_DATE_TIME) DESC , sub.SYSTEM_OWNER , sub.transaction_name , sub.response DESC; 

Я также добавил индексы к моему столу, но все-таки это очень медленно.

Любая помощь в том, как я могу сделать это быстро?

РЕДАКТИРОВАТЬ: Ran вышеупомянутый запрос, используя EXPLAIN

+----+-------------+------------+------+----------------------------+------+---------+------+----------+---------------------------------+ 
| id | select_type | table  | type | possible_keys    | key | key_len | ref | rows  | Extra       | 
+----+-------------+------------+------+----------------------------+------+---------+------+----------+---------------------------------+ 
| 1 | PRIMARY  | <derived2> | ALL | NULL      | NULL | NULL | NULL | 16053297 | Using temporary; Using filesort | 
| 2 | DERIVED  | logs  | ALL | system_owner,response_code | NULL | NULL | NULL |6592 | Using where      | 
+----+-------------+------------+------+----------------------------+------+---------+------+----------+---------------------------------+ 
+0

Вам нужно будет лучше взглянуть на ваши индексы. Я предполагаю, что потребуется немного времени, чтобы разобраться - вам понадобятся они, охватывающие несколько столбцов, так как у вас есть много критериев где/группа/порядок. Я предполагаю, что вы запустили 'EXPLAIN', чтобы посмотреть, с чем он борется? – calcinai

+0

Одна вещь, которую вы могли бы попробовать, - это изменить 'AND DATE (REQUEST_DATE_TIME) BETWEEN '2016-10-26 00:00:00' AND '2016-10-27 00: 00: 00''' 'и REQUEST_DATE_TIME>' 2016-10 -26 'AND REQUEST_DATE_TIME <' 2016-10-27'' –

+0

Следует отметить, что по умолчанию InnoDB не выделяет много ресурсов. Знаете ли вы, что вы используете? – calcinai

ответ

0

Как можно заметить, запрос должен сканировать всю таблицу.

Но первый, давайте проветривать возможную ошибку:

AND DATE(REQUEST_DATE_TIME) BETWEEN '2016-10-26 00:00:00' 
           AND '2016-10-27 00:00:00' 

Дает журналы для два дня - все 26 и все от 27-го. Или это то, что вы действительно хотели? (BETWEEN является включительно.)

Но проблема производительности является то, что индекс не будет использоваться, поскольку request_date_time скрывается внутри функции (DATE).

Перейти вперед лучший способ выразить это:

AND REQUEST_DATE_TIME >= '2016-10-26' 
AND REQUEST_DATE_TIME < '2016-10-26' + INTERVAL 1 DAY 
  • DATETIME можно сравнить с датой.
  • Полуночное утро 26-го числа включено, но полночь 27-го нет.
  • Вы можете легко изменить 1 к однако много дней вы хотите - без необходимости иметь дело с високосных дней, и т.д.
  • Эта формулировка позволяет использовать индекс на request_date_time, тем самым сокращая строго на количество данных, которые будут отсканированы.

Что касается других заманчивых областей:

  • != не оптимизирует, так что нет «композит» индекс не может быть полезным.
  • Поскольку мы не можем действительно пройти мимо WHERE, индекс не нужен для GROUP BY или ORDER BY.
  • Мои комментарии о DATE() в WHERE не относятся к GROUP BY; никаких изменений не требуется.

Зачем нужен подзапрос? Я думаю, что это можно сделать в одном слое. Это устранит довольно большую таблицу темп. (Да, это означает 3 использования TIMESTAMPDIFF(), но это, вероятно, намного дешевле, чем временная таблица.)

Сколько оперативной памяти? Каково значение innodb_buffer_pool_size?

Если моих комментариев недостаточно, и если вы часто запускаете такой запрос (в течение дня или более диапазона дат), мы можем поговорить о создании и обслуживании Summary table, что может дать вам 10-кратное ускорение.