相对简单的 SQL 查询与 join 拒绝高效
Relatively simple SQL query with join refuses to be efficient
我在优化 SQL(使用 MariaDB)中的某个查询时遇到一些问题,为您提供一些背景信息:我有一个系统 "events"(将它们视为日志条目)可以发生在门票上,但也可以发生在门票之外的其他一些对象上(这就是为什么我将事件和 ticket_event tables 分开的原因)。我想让所有 ticket_event 按 display_time 排序。事件 table 现在有大约 2000 万行。
CREATE TABLE IF NOT EXISTS `event` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`type` varchar(255) DEFAULT NULL,
`data` text,
`display_time` datetime DEFAULT NULL,
`created_time` datetime DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `index_for_display_time_and_id` (`id`,`display_time`),
KEY `index_for_display_time` (`display_time`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
CREATE TABLE IF NOT EXISTS `ticket_event` (
`id` int(11) NOT NULL,
`ticket_id` int(11) NOT NULL,
PRIMARY KEY (`id`),
KEY `ticket_id` (`ticket_id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
ALTER TABLE `ticket_event`
ADD CONSTRAINT `ticket_event_ibfk_1` FOREIGN KEY (`id`) REFERENCES `event` (`id`),
ADD CONSTRAINT `ticket_event_ibfk_2` FOREIGN KEY (`ticket_id`) REFERENCES `ticket` (`id`);
如你所见,我已经玩弄了一些键(我也为 (id, ticket_id) 制作了一个,自从我再次删除它后现在没有出现在这里)我执行的查询:
SELECT * FROM ticket_event
INNER JOIN event ON event.id = ticket_event.id
ORDER BY display_time DESC
LIMIT 25
执行该查询需要相当长的时间(如果我对特定的 ticket_id 进行过滤,则大约需要 30 秒,如果不对其进行过滤,甚至无法可靠地完成它)。如果我 运行 对查询进行解释,它显示它执行文件排序 + 临时:
我试了一下力指数等,但这似乎没有解决任何问题,或者我做错了。
有人看到我做错了什么或者我可以在这里优化什么吗?我非常不希望通过添加 ticket_id/host_id 等作为列来使 "event" 变宽 table,如果它们不适用则将它们设为 NULL。
提前致谢!
编辑:EXPLAIN
的额外图像,在 table 中有实际行:
您的查询 select 每一行的每一列,即使您使用了 LIMIT。您是否尝试过 select 按 id 的特定行?
KEY `index_for_display_time_and_id` (`id`,`display_time`),
没用;算了吧。它没有用,因为你使用的是 InnoDB,它将数据 "clustered" 存储在 PK (id
).
上
请将ticket_event
.id
改为event_id
。 id
令人困惑,因为它感觉像是映射 table 的 PK,它确实是。可是等等!那没有意义?每个活动只有一张票?那为什么 ticket_event
存在呢?为什么不把 ticket_id
放在 event
中?
对于多对多table,做
CREATE TABLE IF NOT EXISTS `ticket_event` (
`event_id` int(11) NOT NULL,
`ticket_id` int(11) NOT NULL,
PRIMARY KEY (`event_id`, ticket_id), -- for lookup one direction
KEY (`ticket_id`, event_id) -- for the other direction
) ENGINE=InnoDB DEFAULT;
也许你会通过尝试获得更好的性能:
SELECT *
FROM ticket_event
INNER JOIN (select * from event ORDER BY display_time DESC limit 25) as b
ON b.id = ticket_event.id;
好的,如果您尝试强制索引怎么办?
SELECT * FROM ticket_event
INNER JOIN event
FORCE INDEX (index_for_display_time)
ON event.id = ticket_event.id
ORDER BY display_time DESC
LIMIT 25;
我在优化 SQL(使用 MariaDB)中的某个查询时遇到一些问题,为您提供一些背景信息:我有一个系统 "events"(将它们视为日志条目)可以发生在门票上,但也可以发生在门票之外的其他一些对象上(这就是为什么我将事件和 ticket_event tables 分开的原因)。我想让所有 ticket_event 按 display_time 排序。事件 table 现在有大约 2000 万行。
CREATE TABLE IF NOT EXISTS `event` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`type` varchar(255) DEFAULT NULL,
`data` text,
`display_time` datetime DEFAULT NULL,
`created_time` datetime DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `index_for_display_time_and_id` (`id`,`display_time`),
KEY `index_for_display_time` (`display_time`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
CREATE TABLE IF NOT EXISTS `ticket_event` (
`id` int(11) NOT NULL,
`ticket_id` int(11) NOT NULL,
PRIMARY KEY (`id`),
KEY `ticket_id` (`ticket_id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
ALTER TABLE `ticket_event`
ADD CONSTRAINT `ticket_event_ibfk_1` FOREIGN KEY (`id`) REFERENCES `event` (`id`),
ADD CONSTRAINT `ticket_event_ibfk_2` FOREIGN KEY (`ticket_id`) REFERENCES `ticket` (`id`);
如你所见,我已经玩弄了一些键(我也为 (id, ticket_id) 制作了一个,自从我再次删除它后现在没有出现在这里)我执行的查询:
SELECT * FROM ticket_event
INNER JOIN event ON event.id = ticket_event.id
ORDER BY display_time DESC
LIMIT 25
执行该查询需要相当长的时间(如果我对特定的 ticket_id 进行过滤,则大约需要 30 秒,如果不对其进行过滤,甚至无法可靠地完成它)。如果我 运行 对查询进行解释,它显示它执行文件排序 + 临时:
我试了一下力指数等,但这似乎没有解决任何问题,或者我做错了。
有人看到我做错了什么或者我可以在这里优化什么吗?我非常不希望通过添加 ticket_id/host_id 等作为列来使 "event" 变宽 table,如果它们不适用则将它们设为 NULL。
提前致谢!
编辑:EXPLAIN
的额外图像,在 table 中有实际行:
您的查询 select 每一行的每一列,即使您使用了 LIMIT。您是否尝试过 select 按 id 的特定行?
KEY `index_for_display_time_and_id` (`id`,`display_time`),
没用;算了吧。它没有用,因为你使用的是 InnoDB,它将数据 "clustered" 存储在 PK (id
).
请将ticket_event
.id
改为event_id
。 id
令人困惑,因为它感觉像是映射 table 的 PK,它确实是。可是等等!那没有意义?每个活动只有一张票?那为什么 ticket_event
存在呢?为什么不把 ticket_id
放在 event
中?
对于多对多table,做
CREATE TABLE IF NOT EXISTS `ticket_event` (
`event_id` int(11) NOT NULL,
`ticket_id` int(11) NOT NULL,
PRIMARY KEY (`event_id`, ticket_id), -- for lookup one direction
KEY (`ticket_id`, event_id) -- for the other direction
) ENGINE=InnoDB DEFAULT;
也许你会通过尝试获得更好的性能:
SELECT *
FROM ticket_event
INNER JOIN (select * from event ORDER BY display_time DESC limit 25) as b
ON b.id = ticket_event.id;
好的,如果您尝试强制索引怎么办?
SELECT * FROM ticket_event
INNER JOIN event
FORCE INDEX (index_for_display_time)
ON event.id = ticket_event.id
ORDER BY display_time DESC
LIMIT 25;