我试图在一个非常大的表上运行以下查询,其中超过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
如何优化此功能?
感谢您的回答
答案 0 :(得分:3)
DATE_FORMAT(time_start '%Y-%m-%d')
听起来很贵
列上的每个计算都会减少索引的使用。您可能会为每个值运行完整的索引扫描+ DATE_FORMAT
计算,而不是索引查找/范围扫描。
尝试将计算值存储在列中(或者如果mysql支持,则创建计算索引)。或者甚至更好地重写条件,直接与列中存储的值进行比较。
答案 1 :(得分:2)
嗯,90ml很多,但是我怀疑它没有使用start_time_idx
因为操作,你可以避免(你可以操纵你比较它的值,它也必须完成如果mysql足够智能,每个查询只有一次),你检查过EXPLAIN
吗?
答案 2 :(得分:1)
您可能希望按time_start
分组和排序,而不是在运行查询时创建的period
值。按period
排序需要在完成任何排序之前生成所有这些值。
答案 3 :(得分:1)
尝试使用以下内容交换WHERE子句:
WHERE account_id = -2 AND time_start BETWEEN CURDATE() - INTERVAL 90 DAY AND CURDATE()
MySQL仍然会捕捉到它们之间的日期,唯一需要担心的是今天的日期,由于技术上大于午夜,它们可能会被截断。
您可以通过使用CURDATE( )
CURDATE( ) + INTERVAL 1 DAY
来解决此问题
答案 4 :(得分:0)
我要改变
BETWEEN CURDATE() - INTERVAL 90 DAY AND CURDATE()
到
> (CURDATE() - INTERVAL 90 DAY)
你以后没有记录,是吗?
答案 5 :(得分:0)
将查询更改为:
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(time_start)
效果不佳 - 添加date_start
列并存储time_start
的日期部分。
然后在(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