AWS RDS MySql 具有复合索引和日期范围的简单查询在大约 800 万个数据中执行时间过长

Posted

技术标签:

【中文标题】AWS RDS MySql 具有复合索引和日期范围的简单查询在大约 800 万个数据中执行时间过长【英文标题】:AWS RDS MySql Simple query with composite index and date range took too long to execute out of ~8 millions data 【发布时间】:2020-04-16 17:50:49 【问题描述】:

查询非常简单,即

SELECT 
    col1 , date_col 
FROM table USE INDEX (device_date_col) 
WHERE 
    device_id = "some_value"
    AND date_col BETWEEN "2020-03-16 00:00:00" and "2020-04-16 00:00:00" 
limit 1000000 ;

但第一次运行时最终返回结果需要 30 到 60 秒。然后它在 10 秒内返回结果。另一个问题是,当我再次更改 device_id 时,需要很长时间。除了使用正确的索引之外,我无法理解为什么会发生这种情况。

我们知道,由于我们的 API 遇到超时,API 网关有 30 秒的限制。从今天起突然发生。

主要目标是检索微小的数据,它返回的数据较少但也需要很长时间,即

 ....
 AND col1 IS NOT NULL
 GROUP BY 
    DATE(date_col),
    HOUR(date_col), 
    MINUTE(date_col)

以下是一些有用的信息

    AWS RDS 具有实例 db.m4.large(vCPU 2 和 RAM 8GB)。 mysql 版本 5.6.x date_col 和 device_col 上的复合索引 使用 InnoDB 表没有id字段(主键) 表中的总行数为 750 万 每台设备每 3 秒有一次数据 查询返回大约 600k 的行 解释查询显示它正在使用索引

更新

MySql Workbench 显示,当我在没有 group by 的情况下运行查询时,执行需要 2 秒,但检索时间 > 30 秒,当我使用 group by 时,服务器需要 > 30 来执行但检索需要 2 秒。 我认为我们需要更多

CPU 使用 group by 处理数据 更多内存用于提取所有数据(无分组)

下图显示了没有分组依据的查询响应。查看持续时间/获取时间

【问题讨论】:

你试过不使用索引吗?您处于它对您的情况有用的边缘,请参见例如here。尝试删除USE INDEX (device_date_col),或者,最有希望的是,在索引中包含 col1,例如添加索引(device_id, date_col, col1)。第二次运行比第一次运行更快是由于数据在内存中,而不必从磁盘读取。除了增加缓冲池(这可能需要您获得更多内存)或确保所有数据都在内存中(通过之前使用它)之外,您无能为力。 @Solarflare 我已经更新了问题,请您查看并建议我的假设? 我们已经确保第一个查询不应该超时,即它必须在 30 秒以下。 SQL_NO_CACHE 未按预期工作。每次我运行查询时,时间总是比以前少,这让我相信它仍然显示缓存的结果。还尝试了 RESET QUERY CACHE;刷新查询缓存; @Solarflare,我已经按照您的建议创建了新索引(device_id、date_col、col1)。在有/没有 group by 的情况下,现在似乎工作正常。谢谢 【参考方案1】:

(原始查询)

SELECT  col1 , date_col
    FROM  table USE INDEX (device_date_col)
    WHERE  device_id = "some_value"
      AND  date_col BETWEEN "2020-03-16 00:00:00"
                        AND "2020-04-16 00:00:00"
    limit  1000000 ;

INDEX(device_id, date_col, col1)的讨论

    = 列、名称、y device_id 开始索引。这在一定程度上集中了搜索。 在此范围内,进一步关注日期范围。因此,将date_col 添加到索引中。您现在拥有WHERE 的最佳索引 如果列数不多且不包含TEXT 列,则添加SELECT 中任何位置显示的所有其他列。现在你有了一个“覆盖”索引。这允许仅使用索引的BTree 执行查询,从而进一步提高速度。

更多讨论:http://mysql.rjweb.org/doc.php/index_cookbook_mysql

其他说明

没有ORDER BYLIMIT 通常是没有意义的——你有可能得到一组随机的行。

BETWEEN 包含一个额外的午夜。我建议

AND  date_col >= "2020-03-16"
AND  date_col  < "2020-03-16" + INTERVAL 1 MONTH

删除USE INDEX -- 今天可能会有所帮助,但明天可能会在数据更改或常量更改时造成伤害。

LIMIT 1000000 -- 这可能会让一些客户窒息。你真的需要那么多行吗?或许可以在数据库中进行更多处理? 添加GROUP BY -- 在某些分钟内col1 是否有两个值?你会得到col1 的哪个值?考虑MAX(col1)ANY_VALUE(col1)GROUP_CONCAT(DISTINCT col1)

【讨论】:

以上是关于AWS RDS MySql 具有复合索引和日期范围的简单查询在大约 800 万个数据中执行时间过长的主要内容,如果未能解决你的问题,请参考以下文章

具有日期范围的 CoreData 复合提取的意外结果

如何使用 mySQL 工作台连接到 AWS 私有子网 VPC 中的 RDS

AWS RDS下MySQL ADD COLUMN慢

MySQL联合索引生效的条件、索引失效的条件

Pandas 获取具有复合索引的数据帧的行号

MYSQL:复合索引比单独索引有什么好处?