【问题标题】:MySQL query optimizationMySQL查询优化
【发布时间】:2011-07-07 07:53:31
【问题描述】:

您能帮我优化这个查询吗?我花了很多时间,但仍然无法将其改写为足够快(比如在几秒钟内运行,而不是现在的几分钟)。

查询:

SELECT m.my_id, m.my_value, m.my_timestamp
  FROM (
    SELECT my_id, MAX(my_timestamp) AS most_recent_timestamp
      FROM my_table
      WHERE my_timestamp < '2011-03-01 08:00:00'
      GROUP BY my_id
  ) as tmp
LEFT OUTER JOIN my_table m
ON tmp.my_id = m.my_id AND tmp.most_recent_timestamp = m.my_timestamp
ORDER BY m.my_timestamp;

my_table 定义如下:

CREATE TABLE my_table (
   my_id INTEGER NOT NULL,
   my_value VARCHAR(4000),
   my_timestamp TIMESTAMP default CURRENT_TIMESTAMP NOT NULL,
   INDEX MY_ID_IDX (my_id),
   INDEX MY_TIMESTAMP_IDX (my_timestamp),
   INDEX MY_ID_MY_TIMESTAMP_IDX (my_id, my_timestamp)
);

此查询的目标是在某个时间戳之前为每个 my_id 选择最近的 my_valuemy_table 包含约 1 亿个条目,执行它需要约 8 分钟。

解释:

+----+-------------+-------------+--------+-------- ----------------------------------------------------+--------- ----------------+----------+------------------------ ----+--------+-------------------------- --+ |编号 |选择类型 |表|类型 |可能的键 |关键 | key_len |参考 |行 |额外 | +----+-------------+-------------+--------+-------- ----------------------------------------------------+--------- ----------------+----------+------------------------ ----+--------+-------------------------- --+ | 1 |初级 | |全部 |空 |空 |空 |空 | 90721 |使用临时的;使用文件排序 | | 1 |初级 |米 |参考 | MY_ID_IDX,MY_TIMESTAMP_IDX,MY_ID_TIMESTAMP_IDX | MY_TIMESTAMP_IDX | 4 | tmp.most_recent_timestamp | 1 |使用位置 | | 2 |派生 |我的表 |范围 | MY_TIMESTAMP_IDX | MY_ID_MY_TIMESTAMP_IDX | 8 |空 | 61337 |使用哪里;使用索引进行分组 | +----+-------------+-------------+--------+-------- ----------------------------------------------------+--------- --------------+---------+-------------- --+--------+----------------------------------------------------+

【问题讨论】:

  • 您确定这是您发布的查询的查询计划吗?该计划提到表nv,但查询中没有这样的表。查询甚至可能不正确,因为子选择中 my_id 的值可能不是(实际上不太可能是)my_timestamp = MAX(my_timestamp) 所在行的 id。
  • 哪个版本的mysql?以及为什么删除表名 1。
  • 你的加入条件不应该是...AND tmp.most_recent_timestamp = m.my_timestamp...吗?内部查询似乎也缺少GROUP BY
  • 您能解释一下“SELECT my_id, MAX(my_timestamp) AS ..” 的作用以及为什么没有分组依据吗?
  • @outis,对不起。我已经修改了生产数据库中的原始解释,可能存在不一致之处。我试图纠正它们。| @Joe Stefanelli,是的。似乎我在准备和 SCCE 时错过了它。 @Zimbabao,mysql 5.1 版。

标签: mysql sql query-optimization


【解决方案1】:

如果我理解正确,您应该能够完全删除嵌套选择,并将 where 子句移至主查询,按 my_timestamp 降序排列并限制 1。

SELECT my_id, my_value, max(my_timestamp)
FROM my_table
WHERE my_timestamp < '2011-03-01 08:00:00'
GROUP BY my_id

*edit - 添加最大值和分组依据

【讨论】:

  • 将 ORDER BY 改为 DESC,这样就完美了。
  • 唯一的问题是我们需要所有my_ids 的最新条目。我认为这个查询只会产生一个结果。
  • @Ike,对于每个my_id,我想选择最近的value。所以我需要count(distinct my_id) 结果。
  • 尝试在选择中添加max(my_timestamp)group by my_id 并删除限制
  • @sreimer,这将如何选择my_value对应max(my_timestamp)
【解决方案2】:

我注意到在解释计划中优化器使用 MY_ID_MY_TIMESTAMP_IDX 索引进行子查询,而不是外部查询。

您可以使用索引提示来加快速度。我还更新了 ON 子句以使用其别名来引用 tmp.most_recent_timestamp。

SELECT m.my_id, m.my_value, m.my_timestamp
  FROM (
    SELECT my_id, MAX(my_timestamp) AS most_recent_timestamp
      FROM my_table
      WHERE my_timestamp < '2011-03-01 08:00:00'
      GROUP BY my_id
  ) as tmp
LEFT OUTER JOIN my_table m use index (MY_ID_MY_TIMESTAMP_IDX)
ON tmp.my_id = m.my_id AND tmp.most_recent_timestamp = m.my_timestamp
ORDER BY m.my_timestamp;

【讨论】:

  • @Ike,我已经更正了查询。在准备 SCCE 时错过了 group by 声明。问题是我必须为每个my_id 获取“最新时间戳”。
  • @Ike,不幸的是我自己尝试过,但这并没有改变优化器的行为。据我了解,这是 MySQL 的一项功能 (mysqlperformanceblog.com/2006/08/31/…)。在这一点上,我认为仍然可以在不创建临时表或视图的情况下调整查询。
  • @Alex,如果USE INDEX 没有帮助,请改用FORCE INDEX。您应该能够强制它对表 m 使用 MY_ID_MY_TIMESTAMP_IDX,这可以大大加快您的查询速度。
  • @Ike,不幸的是FORCE INDEX 不会改变任何东西。据我了解 MySQL 创建临时表并使用它进行连接。该表没有索引。我已经尝试了很多东西,似乎应该从另一个角度解决这个问题,因为即使我设法在一个有 4000 万行的表上加速这个查询,它仍然需要在有 4 亿行的表上花费很长时间(刚刚在这张桌子上测试过)。我接受你的回答不是因为它解决了我的问题,而是因为它对我帮助最大。感谢您的宝贵时间。
  • @Alex,我的回答基于您发布的解释计划,我相信可以通过使用索引提示来改进。即使连接的左侧是临时表,右侧是基表,这也是您可以优化的部分。使用索引提示应该改进访问连接右侧(表 m)的方式。你试过了吗?
【解决方案3】:

获取最新记录的技巧是使用 order by 和 'limit 1' 而不是 max aggregation"self" join

类似这样的东西(未测试):

SELECT m.my_id, m.my_value, m.my_timestamp
FROM my_table m
WHERE my_timestamp < '2011-03-01 08:00:00'
ORDER BY m.my_timestamp DESC
LIMIT 1
;
上面的

update 不起作用,因为需要分组...
具有 WHERE-IN-SubSelect 而不是您使用的 JOIN 的其他解决方案。
可能会更快。请用您的数据进行测试。

SELECT m.my_id, m.my_value, m.my_timestamp
FROM my_table m
WHERE ( m.my_id, m.my_timestamp ) IN (
  SELECT i.my_id, MAX(i.my_timestamp)
  FROM my_table i
  WHERE i.my_timestamp < '2011-03-01 08:00:00'
  GROUP BY i.my_id
  )
ORDER BY m.my_timestamp;

【讨论】:

猜你喜欢
  • 2011-01-22
  • 2018-12-21
  • 2010-12-15
  • 2011-11-04
  • 2016-01-25
  • 2023-03-19
相关资源
最近更新 更多