簡體   English   中英

MySQL查詢在大表上運行非常慢

[英]MySQL query runs very slow on large table

我正在嘗試在具有超過9000萬行的超大型表上運行以下查詢

SELECT COUNT(DISTINCT device_uid) AS cnt,  DATE_FORMAT(time_start, '%Y-%m-%d') AS period 
FROM game_session 
WHERE account_id = -2 AND DATE_FORMAT(time_start '%Y-%m-%d') BETWEEN CURDATE() - INTERVAL 90 DAY AND CURDATE()
GROUP BY period 
ORDER BY period DESC

我具有以下表結構:

CREATE TABLE `game_session` (
  `session_id` bigint(20) NOT NULL,
  `account_id` bigint(20) NOT NULL,
  `authentification_type` char(2) NOT NULL,
  `source_ip` char(40) NOT NULL,
  `device` char(50) DEFAULT NULL COMMENT 'Added 0.9',
  `device_uid` char(50) NOT NULL,
  `os` char(50) DEFAULT NULL COMMENT 'Added 0.9',
  `carrier` char(50) DEFAULT NULL COMMENT 'Added 0.9',
  `protocol_version` char(20) DEFAULT NULL COMMENT 'Added 0.9',
  `lang_key` char(2) NOT NULL DEFAULT 'en',
  `instance_id` char(100) NOT NULL,
  `time_start` datetime NOT NULL,
  `time_end` datetime DEFAULT NULL,
  PRIMARY KEY (`session_id`),
  KEY `game_account_session_fk` (`account_id`),
  KEY `lang_key_fk` (`lang_key`),
  KEY `lookup_active_session_idx` (`account_id`,`time_start`),
  KEY `lookup_finished_session_idx` (`account_id`,`time_end`),
  KEY `start_time_idx` (`time_start`),
  KEY `lookup_guest_session_idx` (`device_uid`,`time_start`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1

我該如何優化呢?

謝謝你的回答

DATE_FORMAT(time_start '%Y-%m-%d')聽起來很昂貴。
列上的每個計算都會減少索引的使用。 您可能需要對每個值進行完整的索引掃描+ DATE_FORMAT計算,而不是索引查找/范圍掃描。

嘗試將計算值存儲在列中(如果mysql支持,則創建一個計算索引)。 甚至更好地重寫條件以直接與存儲在列中的值進行比較。

好吧,90mlns太多了,但是我懷疑它不會使用start_time_idx因為可以避免這種操作(您可以操作與之比較的值,如果mysql是,則每個查詢也只能執行一次)足夠聰明),您是否檢查了EXPLAIN

您可能需要對time_start進行分組和排序,而不是根據運行查詢時創建的period值進行分組和排序。 period排序需要先生成所有這些值,然后才能進行任何排序。

嘗試將WHERE子句換成以下內容: WHERE account_id = -2 AND time_start BETWEEN CURDATE() - INTERVAL 90 DAY AND CURDATE()

MySQL仍然會捕捉到它們之間的日期,您唯一需要擔心的是今天的日期,由於技術上大於午夜,所以這些日期可能會被截斷。

您可以通過用CURDATE( ) + INTERVAL 1 DAY增加第二個CURDATE( )來解決此問題

我會改變

BETWEEN CURDATE() - INTERVAL 90 DAY AND CURDATE()

> (CURDATE() - INTERVAL 90 DAY)

您沒有未來的記錄,對嗎?

將查詢更改為:

SELECT COUNT(DISTINCT device_uid) AS cnt
     , DATE_FORMAT(time_start, '%Y-%m-%d') AS period 
FROM game_session 
WHERE account_id = -2 
  AND time_start >= CURDATE() - INTERVAL 90 DAY 
  AND time_start <  CURDATE() + INTERVAL 1 DAY
GROUP BY DATE(time_start) DESC

因此(account_id, time_start)的索引可用於查詢的WHERE部分。


如果仍然很慢date_start DATE(time_start)看起來對性能不太好-添加date_start列並存儲time_start的日期部分。

然后在(account_id, date_start, device_uid)上添加索引(account_id, date_start, device_uid)這將進一步提高性能,因為所有必需的信息-對於GROUP BY date_startCOUNT(DISTINCT device_uid)部分-都將在索引上:

SELECT COUNT(DISTINCT device_uid) AS cnt
     , date_start                 AS period 
FROM game_session 
WHERE account_id = -2 
  AND date_start BETWEEN CURDATE() - INTERVAL 90 DAY 
                     AND CURDATE()
GROUP BY date_start DESC

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM