2016-11-08 66 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 

和它有超过3000条记录。

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; 

我还添加索引到我的表,但它仍然是非常缓慢的。

任何帮助我如何使它快速?

编辑: 然使用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)'2016-10-26 00:00:00'和'2016-10-27 00:00:00''更改为'AND 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 BYORDER BY有用。
  • 我对DATE() in WHERE的评论并不适用于GROUP BY;不需要改变。

为什么有子查询?我认为这可以在单一层完成。这将消除一个相当大的临时表。 (是的,这意味着3使用TIMESTAMPDIFF(),但这可能比临时表便宜很多。)

有多少内存? innodb_buffer_pool_size的值是多少?

如果我的评论还不够,而且如果你频繁地运行一个这样的查询(一天或超过一个日期范围),那么我们可以谈论构建和维护Summary table,这可能会给你10倍的加速。