MySQL选择主键而不是索引

时间:2017-11-27 00:51:46

标签: mysql performance optimization query-performance

我有一个包含大约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命令并返回以下内容:

EXPLAIN results

我不是(到目前为止)数据库专家,但我认为这个查询运行全表扫描(FTS)而不是使用INDEX(start,end,room_id)是很奇怪的。显然,它将“start”标识为可行键,然后忽略它,选择PRIMARY KEY并运行FTS。所以,我跟着this answer,删除了PRIMARY KEY并添加了(start,end,room_id)作为新的PRIMARY KEY,但结果完全相同(甚至最差)。

这是一个必须在平台上执行某些其他操作后立即生成的请求,因此无法对其进行排队并将其作为批处理运行,其他更新会以延迟形式更新。

还有其他方法可以改善此查询的效果吗?

谢谢!

2 个答案:

答案 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的示例。

有趣的是你要按小时租房!