我的应用程序中有一个简单的SQL语句:
SELECT SQL_NO_CACHE key_event_id, MAX(report_ts) AS max_ts
FROM `key_event_reports`
WHERE report_model_id = 2
GROUP BY key_event_id;
key_event_reports
表的中等大小(约17M行),这是表定义:
CREATE TABLE IF NOT EXISTS `key_event_reports` (
`key_event_report_id` int(20) NOT NULL AUTO_INCREMENT,
`report_model_id` int(5) NOT NULL,
`key_event_id` int(5) NOT NULL,
`title_id` int(15) NOT NULL,
`report_ts` datetime NOT NULL,
`report_time` time NOT NULL,
`total` int(7) NOT NULL DEFAULT '0',
`pos` int(7) NOT NULL DEFAULT '0',
`neg` int(7) NOT NULL DEFAULT '0',
`smooth_total` float NOT NULL DEFAULT '0',
`smooth_pos` float NOT NULL DEFAULT '0',
`smooth_neg` float NOT NULL DEFAULT '0',
`buzz` float NOT NULL DEFAULT '0',
`sentiment` float NOT NULL DEFAULT '0',
PRIMARY KEY (`key_event_report_id`),
UNIQUE KEY `key_event_id_4` (`key_event_id`,`report_model_id`,`title_id`,`report_ts`),
KEY `report_model_id` (`key_event_id`,`report_time`),
KEY `report_model_id_2` (`report_model_id`,`key_event_id`,`report_ts`),
KEY `key_event_id` (`key_event_id`,`report_model_id`,`report_time`,`title_id`,`smooth_total`),
KEY `key_event_id_3` (`key_event_id`,`report_model_id`,`report_time`,`title_id`,`smooth_pos`),
KEY `key_event_id_2` (`key_event_id`,`report_model_id`,`report_time`,`title_id`,`smooth_neg`),
KEY `get_latest_report` (`report_model_id`,`report_ts`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=16967636 ;
report_model_id总是2(数据库中还没有其他模型,但这可能很快就会改变)并且每10分钟就会报告10个不同的key_events。
此查询需要很长时间而不进行缓存(大约20秒)。当上面的查询在更大的语句中用作子查询时,问题变得更糟:
SET @report_model_id = 2;
SET @message_id = ?;
SET @title_id = ?
SET @min_score = 5;
SET @min_message_id = (
SELECT MIN(message_id)
FROM `messages`
WHERE msg_time > DATE_SUB(NOW(), INTERVAL 20 MINUTE)
);
SELECT
ke.key_event_id AS key_event_id,
COALESCE(kermmid.message_id, MIN(mhke.message_id)) AS max_message_id,
ker_max.max_ts AS last_report_ts
FROM `key_events` ke
LEFT JOIN (
SELECT key_event_id, MAX(report_ts) AS max_ts
FROM `key_event_reports`
WHERE report_model_id = 2
GROUP BY key_event_id
) ker_max
ON ( ker_max.key_event_id = ke.key_event_id )
LEFT JOIN `key_event_reports` ker
ON (
ker.key_event_id = ke.key_event_id
AND ker.report_model_id = @report_model_id
AND ker.title_id = @title_id
AND ker.report_ts = @actcurrent
)
LEFT JOIN `key_event_report_max_message_ids` kermmid
ON (
kermmid.key_event_id = ker.key_event_id
AND kermmid.report_model_id = ker.report_model_id
AND kermmid.report_ts = ker.report_ts
)
LEFT JOIN `messages_has_key_events` mhke
ON (
mhke.key_event_id = ke.key_event_id
AND mhke.title_id = @title_id
AND mhke.message_id > @min_message_id
AND mhke.message_id < @message_id
AND mhke.score > @min_score
)
GROUP BY
ke.key_event_id;
如果我在这里使用子查询,执行时间也会从~50ms到> 20s。
这可能是什么原因,我怎么可能优化我的陈述或数据库结构?
答案 0 :(得分:3)
尝试在(report_model_id,key_event_id,report_ts)
上添加索引并将report_model_id
添加到群组中。这应该允许它使用group by optimization。
SELECT key_event_id, MAX(report_ts) AS max_ts
FROM `key_event_reports`
WHERE report_model_id = 2
GROUP BY report_model_id, key_event_id
我仍在尝试为查询的其余部分找到一种方法......内部SELECT需要是LEFT JOIN还是INNER JOIN会做什么?
编辑:我错过了你已经拥有索引的事实,所以你只需要将字段添加到GROUP BY。
答案 1 :(得分:2)
对于“为什么”,我的猜测是MySQL查询缓存。
MySQL会在某些情况下缓存查询结果,以加快重复查询。如果数据发生更改,则必须重新运行查询。我不知道它是如何处理子查询的。
答案 2 :(得分:1)
您的查询似乎已在使用此索引。
`report_model_id_2` (`report_model_id`,`key_event_id`,`report_ts`)
它包含您的查询所需的所有信息,因此MySQL可以通过对此索引而不是整个表进行范围扫描来满足您的查询。好消息是你已经很好地优化了查询。这也是坏消息。
创建摘要表是否有意义,并在MySQL数据库中设置事件以便偶尔更新详细数据中的摘要表?如果对于您的应用程序而言,如果此查询的结果稍稍落后,则只会出现这种情况。
如果您必须将此信息与详细信息表格完美地同步,您还可以设置更新摘要表的触发器。