我的查询存在性能问题。 这是表模式:
CREATE TABLE `file_info` (
`FILE_NAME` varchar(255) DEFAULT '',
`START_TIME` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
`END_TIME` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
`FILE_SIZE` int(10) NOT NULL DEFAULT '0',
`LOG_SERVER_NAME` varchar(255) NOT NULL DEFAULT '',
`PHASE` varchar(255) NOT NULL DEFAULT '',
`APPLICATION` varchar(255) NOT NULL DEFAULT '',
`TYPE` varchar(255) NOT NULL DEFAULT '',
`FULLPATH` varchar(255) NOT NULL DEFAULT '',
`COMPRESSED` tinyint(1) NOT NULL DEFAULT '0',
`CLOSED` tinyint(1) NOT NULL DEFAULT '0',
`ARCHIVED_PATH` varchar(255) NOT NULL DEFAULT '',
`FILE_TYPE` varchar(45) NOT NULL DEFAULT '',
PRIMARY KEY (`LOG_SERVER_NAME`,`FULLPATH`),
UNIQUE KEY `uk_file_info` (`LOG_SERVER_NAME`,`FULLPATH`,`APPLICATION`) USING BTREE,
KEY `IDX_STARTTIME` (`START_TIME`),
KEY `IDX_ENDTIME` (`END_TIME`),
KEY `IDX_PHASE` (`PHASE`),
KEY `IDX_APLICATION` (`APPLICATION`),
KEY `IDX_LOGSERVERNAME` (`LOG_SERVER_NAME`),
KEY `IDX_FULLPATH` (`FULLPATH`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1;
这是我的问题:
SELECT * FROM FILE_INFO
WHERE PHASE ='DEV'
AND APPLICATION ='SIALT'
AND ((START_TIME <'2014-11-11 08:17:00' AND END_TIME >'2014-11-11 08:17:00')
OR (START_TIME <'2014-11-11 08:22:00' AND END_TIME >'2014-11-11 08:22:00')
OR (START_TIME >'2014-11-11 08:17:00' AND END_TIME <'2014-11-11 08:22:00'))
查询任务的时间很少。有时超过30秒。
我已将索引放在我要过滤的字段上。 我正在使用MyISAM,因为我知道如果DB没有外键会更好。
所以我正在寻找新的想法来改进我的查询。它今天几乎无法使用。
添加自动增量键会有帮助吗?即使我不按ID过滤? 从MyISAM改为InnoDB?
修改
解释给出
id 1
select_type SIMPLE
table FILE_INFO
type ref
possible_keys IDX_STARTTIME,IDX_ENDTIME,IDX_PHASE,IDX_APLICATION
key IDX_APLICATION
key_len 257
ref const
rows 756718
Extra Using index condition; Using where
我会尝试其他建议并更新我的帖子。
感谢您的提示。
罗曼。
答案 0 :(得分:1)
您应该使用RANGE BASED分区,根据日期创建分区,可能是一个月或一周,这将提升性能。
答案 1 :(得分:1)
您应该将VARCHAR列的大小减小到可以的最小大小。虽然VARCHAR仅通过使用所需的内容节省了数据页面存储空间,但索引条目仍然使用最大值。对于latin1 VARCHAR(255)列,每行的255个字节。 您的主键大小为512字节。
在改进列的大小后,以下三列上的多列索引将非常适合读取速度(phase, application, start_time)
。我们不包括end_time,因为你的复合指数只能达到第一个范围。在阶段和应用之间,首先要有更高的基数(更多的唯一性)。将索引保留在end_time上,因为MySQL可以使用索引合并优化。
然后,为了帮助MySQL退出并让它执行范围扫描,请将OR
转换为UNION ALL
。
SELECT * FROM FILE_INFO
WHERE PHASE ='DEV'
AND APPLICATION ='SIALT'
AND (START_TIME <'2014-11-11 08:17:00' AND END_TIME >'2014-11-11 08:17:00')
UNION ALL
SELECT * FROM FILE_INFO
WHERE PHASE ='DEV'
AND APPLICATION ='SIALT'
AND (START_TIME <'2014-11-11 08:22:00' AND END_TIME >'2014-11-11 08:22:00')
UNION ALL
SELECT * FROM FILE_INFO
WHERE PHASE ='DEV'
AND APPLICATION ='SIALT'
AND (START_TIME >'2014-11-11 08:17:00' AND END_TIME <'2014-11-11 08:22:00')
根据您的数据,您可能还需要强制MySQL使用多列索引(而不是end_time上的索引)。
这样的大型索引需要大量的RAM(整个索引需要在内存中始终保持快速),以及正确的MySQL配置。
答案 2 :(得分:1)
感谢所有建议。
我更改了索引以包含主键中的所有where子句。这还不够。
我看到基于datetime的分区未正确使用。 所以我所做的就是创建一个新的日期字段(基于start_time)。分区对日期很有效(没有时间)。
以下是最终查询:
SELECT * FROM FILE_INFO
WHERE PHASE ='PDT' AND APPLICATION ='SIALT'
AND FILE_DATE = '2014-12-10'
AND ((START_TIME <'2014-12-10 08:17:00' AND END_TIME >'2014-12-10 08:17:00')
OR (START_TIME <'2014-12-10 17:22:00' AND END_TIME >'2014-12-10 17:22:00')
OR (START_TIME >'2014-12-10 08:17:00' AND END_TIME <'2014-12-10 17:22:00'))
我认为start_time上的索引由于某种原因而无法正常工作。我可以解释非常糟糕的表现。