【问题标题】:Slow MySQL query when using LIKE operation on large table在大表上使用 LIKE 操作时 MySQL 查询慢
【发布时间】:2014-10-21 17:37:00
【问题描述】:

我有一个相当大的表(~ 6 GB),我在这个查询中遇到了性能问题:

          SELECT f.*,
          TIME_FORMAT(f.scheme, '%H:%i') as scheme,
          TIME_FORMAT(f.actual, '%H:%i') as actual,
          DATE_FORMAT(f.flight_date, '%d-%m-%Y') as flight_date_formatted,
          a.iata
          FROM flights_database f
          LEFT JOIN airports a ON f.airport = a.airportNameClean
          WHERE f.flight_date BETWEEN DATE_SUB(CURDATE(), INTERVAL 30 DAY)
          AND DATE_ADD(CURDATE(), INTERVAL 2 DAY)
          AND (f.flight_number LIKE 'New York%' OR f.airport LIKE 'New York%' OR f.airline LIKE 'New York%')
          ORDER by f.flight_date DESC, f.flight_scheme DESC
          LIMIT 50"

我使用过EXPLAIN 并发现了这些潜在问题

  • 使用多个 LIKE 和 OR 让它使用一个范围(使用 WHERE)的记录,似乎会使其变慢
  • f.flight_scheme DESC,添加时使用文件排序。删除后,不使用文件排序。

我在flight_date, flight_number, airport, airline, scheme 上有一个索引,它报告使用它。 但是这个查询仍然需要大约 30 秒,这太离谱了。

使用某种子查询来替换 OR 部分可能会有所帮助。但是,在运行子查询后,如何确定我实际需要搜索的搜索查询类型(例如,哪一列)。

感谢您的想法和提示。

【问题讨论】:

  • 而不是 LIKE,而是根据位置 id 或某事获取所有可能的去纽约的 filght_number,然后执行 JOIN,其他列的类似操作。
  • 如果您不能使用 FK 引用城市的系统并使用索引查找或直接 FK 值,那么类似于 'New York' IN (LEFT(f.flight_number,8), LEFT(f.airport,8), LEFT(f.airline,8)) 的结构可能会更好?

标签: mysql sql performance indexing


【解决方案1】:

我相信您当前的索引对于查询来说并不是最优的,主要是因为“或”表达式。您应该创建 3 个索引。

(航班号、航班日期、架构)

(机场、航班日期、模式)

(航空公司、航班日期、架构)

然后将查询更改为使用三个索引。您也可以稍微玩一下,也可以通过添加 order by 和限制为 50 来修剪每个子查询。

select flight.*,
    TIME_FORMAT(flight.scheme, '%H:%i') as scheme,
    TIME_FORMAT(flight.actual, '%H:%i') as actual,
    DATE_FORMAT(flight.flight_date, '%d-%m-%Y') as flight_date_formatted,
    a.iata
from (
    select *
    from (
        select f.Id,
            f.flight_date,
            f.schema
        from flights_database f
        where f.flight_date between DATE_SUB(CURDATE(), INTERVAL 30 DAY)
                and DATE_ADD(CURDATE(), INTERVAL 2 DAY)
            and f.flight_number like 'New York%'
        order by f.flight_date desc,
            f.schema desc limit 50

        union

        select f.Id,
            f.flight_date,
            f.schema
        from flights_database f
        where f.flight_date between DATE_SUB(CURDATE(), INTERVAL 30 DAY)
                and DATE_ADD(CURDATE(), INTERVAL 2 DAY)
            and f.airline like 'New York%'
        order by f.flight_date desc,
            f.schema desc limit 50

        union

        select f.Id,
            f.flight_date,
            f.schema
        from flights_database f
        where f.flight_date between DATE_SUB(CURDATE(), INTERVAL 30 DAY)
                and DATE_ADD(CURDATE(), INTERVAL 2 DAY)
            and f.airport like 'New York%'
        order by f.flight_date desc,
            f.schema desc limit 50
        ) f1
    order by f1.flight_date desc,
        f.schema desc limit 50
    ) f2
inner join flights_database flight on f2.Id = flight.Id
left join airports a on flight.airport = a.airportNameClean;

目前您的 or 语句将扩展为: [flight_date, flight_number], [flight_date, 航空公司], [flight_date, airport]

所以当优化器查看你的索引时,它会匹配 [flight_date, flight_number] 到您当前的索引 [flight_date, flight_number, airport, airport, airport, scheme] (注意它们是如何开始的),但是当它遇到 [flight_date, airport] 时,没有与此表达式匹配的索引。因此优化器会确定它需要进行索引扫描或表扫描。然后它会再次遇到 [flight_date, airport] 它将确定这需要索引扫描或表扫描。

使用三个新索引和新查询,它将三个索引与三个条件匹配,并确定每个索引都需要索引查找(希望如此)。然后我们包含 'scheme' 来保存所有符合条件的行的 id 行查找。

【讨论】:

  • 有趣的方法。由于表大小需要一段时间才能更改索引。会回来汇报的。谢谢。
  • 好的,索引生成速度比我预期的要快。我添加了建议的索引,奇怪的是当我只运行一个子查询(例如对于机场)时,使用的索引是机场列上的索引,而不是新索引。尽管如此,当省略 ORDER BY 子句时查询速度很好。再次添加时,它会再次使用文件排序并使其变慢:(
  • 这很奇怪...你可以尝试提示索引吗? flight_database f 使用索引(composite_index_name)。
  • 是的,看这个截图没有提示和提示。但是文件排序可能不是由于查询的那一部分?您的完整查询在不到 2 秒的时间内运行,这本身就令人印象深刻。 cl.ly/image/1s1D282E1C1x(无提示)cl.ly/image/1k0M0D1H3g3Y(有提示)
  • 您可以尝试的其他方法是更改​​索引顺序。 (航空公司、航班日期、架构)
猜你喜欢
  • 2012-06-20
  • 1970-01-01
  • 1970-01-01
  • 2020-04-23
  • 1970-01-01
  • 1970-01-01
  • 2021-05-07
  • 2015-01-14
  • 2021-09-04
相关资源
最近更新 更多