【问题标题】:MySQL query performance cliff on large table with two IN statements带有两个 IN 语句的大表上的 MySQL 查询性能悬崖
【发布时间】:2015-10-22 01:22:20
【问题描述】:

我有一个不到 2 亿行的 MySQL 表。我有一个看起来像这样的 Django 查询:

foo.objects.filter(field1_id__in=[about 27 items], 
                   field2_id__in=[about 25 values], 
                   field3=value)

我注意到运行此过滤器的页面今天挂起。昨天的页面在大约一秒钟内呈现。随着更多数据的添加,field1 列表会随着时间的推移而增长,而 field2 列表的大小是恒定的。以交互方式处理这些查询,我确定有一个悬崖,如果我只指定 field2“in”的前 9 个值,查询会在大约一秒钟内返回,但如果我移动到 field2 列表中的 10 个值,则查询“永远”挂起。

如此严重的退化有意义吗?没有连接也没有依赖查询,只有一个 WHERE 与 3 个子句 AND 在一起,其中两个是 IN。感觉像一个 MySQL 错误...?或者“这就是 MySQL 的生活?”

编辑:刚刚返回的 10 项 field2_id__in 查询:花了大约 45 分钟!

原始查询

SELECT `mytable`.`id`, `mytable`.`field1_id`, `mytable`.`field2_id`, `mytable`.`field3_id`, `mytable`.`field4_id`, `mytable`.`data` FROM `mytable` WHERE (`mytable`.`field2_id` IN (44942, 42953, 43099, 43330, 45165, 45468, 43518, 45620, 43693, 45760, 43790, 45930, 43885, 46026, 46120, 44158, 46298, 44314, 42204, 46492, 44441, 42327, 44586, 42515, 44726, 44835, 42802) AND `mytable`.`field3_id` IN (3, 17, 696, 150, 170, 51, 6528, 2383, 3342, 2289, 6491, 6375,2070, 6186, 318, 6498, 5197, 6011, 5833, 7803, 5195, 4871, 6928, 6531) AND `mytable`.`field4_id` = 11 )

解释输出

select_type: simple
type: range
possible_keys: (3 keys)
key: (key)
key_len: 4
ref: NULL
rows: 14160 
extra: Using index condition; Using where

【问题讨论】:

  • 两个 cmets:首先,您是否尝试过直接从 MySQL 运行各种版本的查询?二、你有没有试过用EXPLAIN看看执行计划是什么?
  • field_2的data_type是什么?
  • 我都没做过。解释听起来是个好主意。 field1 和 field2 都是外键,但查询在 id 上,所以它是简单的整数比较。
  • 是的,当直接在 MySQL 中执行时,查询会挂起。我运行了解释,但不知道如何解释结果。 select_type: simple, type: range, possible_keys: (3 keys), key: (key), key_len: 4, ref: NULL, rows: 14160 extra: 使用索引条件;使用哪里

标签: mysql django


【解决方案1】:

看起来所有 3 个字段都是 foo 表上的外键。只能使用一个索引,因此请在模型中添加一个包含所有 3 个字段的索引以便使用它。

class Meta:
    index_together = ["field1", "field2", "field3"]

写入性能会受到一点影响,但至少您可以查询数据。每个组合都不需要索引,在我上面提供的索引中,对所有字段的查询,只有 field1 或(field1 和 field2)会使用索引(因为从左到右的所有字段都被使用,而 MySql 可以忽略指数的其余部分)。就个人而言,我从未见过写入性能受到如此大的影响,以至于我后悔在表上放置了几个需要的索引。将索引添加到 2 亿行需要几个小时。

请注意,django 会自动为 ForeignKey 字段生成索引,否则连接会非常缓慢。这就是为什么您的解释输出显示 possible_keys: (3 keys),这些可能是 field1-3 上的索引。

数据库确实会跳出悬崖,并且有一个包含 2 亿行的表,我对您的数据库这样做并不感到惊讶。索引对于使数据库发出嗡嗡声至关重要。

【讨论】:

  • 添加这样的索引(大小/写入性能)有什么影响?我有很多方法可以访问这个表,我不确定我是否要添加大量特殊的索引(但如果这是唯一的选择)。为什么当一个 IN 列表的大小从 9 个元素变为 10 个元素时,性能会从 1 秒下降到 45 分钟?
  • 我会编辑答案,因为这里写的太多了。
猜你喜欢
  • 1970-01-01
  • 2014-01-30
  • 1970-01-01
  • 1970-01-01
  • 2020-02-19
  • 2023-03-05
  • 2020-10-22
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多