2016-11-08 67 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倍的加速。