繁体   English   中英

mysql InnoDb在SELECT查询上非常慢

[英]Mysql InnoDb is very slow on SELECT query

我有一个具有以下结构的mysql表:

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)

现在的问题是它非常慢,select的结果要花一些时间才能从表中获取记录。

我的以下子查询需要近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 | 32106592 | Using where                     |
+----+-------------+------------+------+----------------------------+------+---------+------+----------+---------------------------------+

就目前而言,查询必须扫描整个表。

但首先,让我们发布一个可能的错误:

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更改为任意天数-无需处理leap日等。
  • 此公式允许使用request_date_time上的索引,从而大大减少了要扫描的数据量。

至于其他诱人的领域:

  • !=不能很好地优化,因此没有“复合”索引可能是有益的。
  • 由于我们无法真正超越WHERE ,因此没有索引对于GROUP BYORDER BY是有用的。
  • 我在WHEREDATE()评论不适用于GROUP BY 无需更改。

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

多少内存? innodb_buffer_pool_size的值是innodb_buffer_pool_size

如果我的评论还不够,并且您经常在一天或一个日期范围内运行这样的查询,那么我们可以讨论构建和维护Summary表 ,这可能使您的速度提高10倍。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM