我有一个包含大约600,000个条目的表格,我需要更新一些非常具体的条目。该表是:
ID (primary)
start (datetime)
end (datetime)
room_id (int)
locked (tinyint, 1)
我用(start,end,room_id)创建了一个索引,这为我的SELECT查询带来了巨大的性能提升。但是,现在我正在运行以下查询:
update vacancies
set locked = 1
where start >= '2017-11-28 22:00:00'
and end <= '2017-11-28 23:00:00'
and (room_id = 1234
or room_id in (select other_room
from room_dependencies
where first_room = 1234))
我相信这个查询需要花费很多时间才能完成(~1.5s)。我在MySQL上运行EXPLAIN命令并返回以下内容:
我不是(到目前为止)数据库专家,但我认为这个查询运行全表扫描(FTS)而不是使用INDEX(start,end,room_id)是很奇怪的。显然,它将“start”标识为可行键,然后忽略它,选择PRIMARY KEY并运行FTS。所以,我跟着this answer,删除了PRIMARY KEY并添加了(start,end,room_id)作为新的PRIMARY KEY,但结果完全相同(甚至最差)。
这是一个必须在平台上执行某些其他操作后立即生成的请求,因此无法对其进行排队并将其作为批处理运行,其他更新会以延迟形式更新。
还有其他方法可以改善此查询的效果吗?
谢谢!
答案 0 :(得分:0)
MySQL in
条件没有利用索引。请改为inner join
。
update vacancies
inner join
(select other_room
from room_dependencies
where first_room = 1234) t2
on vacancies.room_id = t2.other_room
set locked = 1
where start >= ''
and end <= '';
并单独使用room_id = 1234
进行其他更新。
答案 1 :(得分:0)
虽然您的起始值和结束值可能指定了少量行,但由于索引中的范围由2个单独的属性表示,因此DBMS首先必须使用start >= '2017-11-28 22:00:00'
读取每个索引条目,然后才能丢弃行哪个不满足end <= '2017-11-28 23:00:00'
。 DBMS知道这将是一项昂贵的操作。
有2条路线可以解决这个问题。一种方法是在预定义的一组块中划分范围,然后将每个块连接回空位表(这很麻烦,但效率仍然不高)。
正确的解决方案是将时间视为一维空间并应用geo-spatial indexing。在MySQL中,地理空间坐标只能有2个维度 - 因此您需要将时间范围映射到2D空间。互联网上有很多关于如何做到这一点with IP addresses的例子,但我没有看到任何与MySQL和日期范围有关的具体内容。以下是using SQL Server的示例。
有趣的是你要按小时租房!