我从内部联接中获得了非常出乎意料的糟糕性能,我不知所措,因为所有必要的索引似乎都就位,并且如果将其拆分为2个查询,它的运行速度至少快50倍。但是对我而言,关系数据库应该擅长的正是这种确切的查询类型。 MySQL 5.6是否重要。帐户表有约4K条记录,事件表有约500M条记录。
CREATE TABLE `tbl_account` (
`accountID` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
`accountKey` varchar(767) CHARACTER SET latin1 COLLATE latin1_general_ci NOT NULL,
...,
PRIMARY KEY (`accountID`),
KEY `index_accountKey` (`accountKey`),
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
CREATE TABLE `tbl_event` (
`eventID` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
`fkAccount` bigint(20) unsigned DEFAULT NULL,
`creationDate` datetime NOT NULL,
...,
PRIMARY KEY (`eventID`),
KEY `index_fkAccount` (`fkAccount`),
KEY `index_creationDate` (`creationDate`),
KEY `index_fkAccount_creationDate` (`fkAccount`, `creationDate`),
CONSTRAINT `event_fkAccount` FOREIGN KEY (`fkAccount`) REFERENCES `tbl_account` (`accountID`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
当我运行以下查询时,它至少需要5.5秒,有时需要近一分钟。
SELECT e.* FROM tbl_event e INNER JOIN tbl_account a ON e.fkAccount = a.accountID
WHERE a.accountKey = 'abcdefghij'
AND e.creationDate >= '2019-02-01 00:00:00'
ORDER BY e.creationDate DESC
LIMIT 500;
如果我首先独立地查找accountID,则它始终只对第一个查询花费0.03s,对第二个查询花费0.1s(当我告诉它使用复合索引时,mysql有时只想使用index_creationDate独自)。
SELECT a.accountID FROM tbl_account a WHERE a.accountKey = 'abcdefghij';
/*returns accountID = 123*/
SELECT e.* FROM tbl_event e USE INDEX (index_fkAccount_creationDate)
WHERE e.fkAccount IN (123)
AND e.creationDate >= '2019-02-01 00:00:00'
ORDER BY e.creationDate DESC
LIMIT 500;
一些注意事项:
使用联接从选择中获得EXPLAIN的输出:
table | type | key | ref |rows| Extra
tbl_account | ref | index_accountKey | const |001 | Using where; Using temporary; Using filesort
tbl_event | ref | index_fkAccount_creationDate | tbl_account.accountID |116 | Using index condition
为什么EXPLAIN说它将是“在哪里使用;使用临时文件;使用文件排序”来从tbl_account返回1行?当我自己查找tbl_account行时,它只是“在哪里使用”。我没有从tbl_account返回任何行作为订单的一部分,那么它试图排序的原因是什么?它不应该只是在tbl_account中查找accountID,然后将其插入tbl_event并在其中使用索引吗?
我尝试将过滤器放入ON子句。我试过使用子查询,但没有任何改善。这是MySQL的最差的优化,是否需要拆分查询,或者我可以做些什么来使其在联接中快速运行?
答案 0 :(得分:0)
我很确定您的查询是
SELECT e.*
FROM tbl_event e
INNER JOIN tbl_account a ON e.fkAccount = a.accountID
WHERE a.accountKey = 'abcdefghij'
AND e.creationDate >= '2019-02-01 00:00:00'
ORDER BY e.creationDate DESC
LIMIT 500;
必须对tbl_event表进行表扫描(由creation_date限定),然后对于每一行,找到匹配的tbl_account行,并将给定值与tbl_account.accountKey值进行比较。
最好按如下所示逆转查询中表的顺序:
SELECT e.*
FROM tbl_account a
INNER JOIN tbl_event e ON e.fkAccount =a.accountID
WHERE a.accountKey='k3'
ORDER BY e.creationDate DESC
LIMIT 500;
这应该首先选择与给定密钥匹配的tbl_account行,然后找到与给定密钥对应的tbl_event行,它们是在给定时间戳记或之后创建的。
这是我测试的sqlfiddle link。