[英]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_start
和COUNT(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.