Mysql select on indexed column slowdown on large tables
Posted
技术标签:
【中文标题】Mysql select on indexed column slowdown on large tables【英文标题】:Mysql select on indexded column slowdown on large tables 【发布时间】:2011-09-09 01:31:46 【问题描述】:我有两个表:A - 301 列(第一个名为 a1 int(11) 主键,第 2 到第 301 个 - double(15,11))和 B - 33 列(第一个 - b1 int(11) 唯一Key, 2nd One - b2 varchar(100) Primary Key, ... , 33rd - b33 int(11) MUL)。
A 和 B 都有约 13,500,000 条记录。
我的 mysql 查询:对于 pos 的每个值,将 pos 设置为 (1, 1000, 2000, ..., 13500000) 中的 1000 的倍数:
select A.*, b2, b5, b7, b8, b10, b13, b33 from A join B on a1=b1 where b33 >= pos and b33
对于 b33 = 8,000,000 时,查询开始需要 60-70 秒。我不明白为什么会出现放缓。 b33 已编入索引,连接发生在一个表中定义为主键且在另一个表中唯一的键上。有解决方法吗?这确实妨碍了代码的速度,如果没有其他方法,我将不得不将表 A 和 B 拆分为几个较小的表。我真的希望我不必那样做!请帮忙!
编辑:这是 EXPLAIN 的 o/p -
************* 1. 行 ********** em>*** 编号:1 选择类型:简单 表:B 类型:范围 可能键:b1,b33 键:b33 key_len: 4 参考:空 行数:981 额外:使用 where************ * 2. 行 ************* 编号:1 选择类型:简单 表:A 类型:eq_ref 可能的键:主要 键:初级 key_len: 4 参考:DBName.B.b1 行数:1 额外: 2 行(0.00 秒)
【问题讨论】:
那么您是否有一个特定的值 > 600,000,其性能突然从 ~5s 下降到 ~20s? 没有。我有更多的范围。发布 650,000 是一场噩梦。 我想这是慢速解释的输出。还有那个快的?有相同的输出吗? 【参考方案1】:由于您的数据库有几百万条记录,您是否采取了措施来保持数据库的健康?
如果您的数据频繁更改(可能是大量插入?),每晚运行以下命令可能有助于提高一般响应能力:
mysqlcheck --check --analyze --auto-repair --all-databases --silent
虽然我会在运行命令之前建议reading up a bit on mysqlcheck,只是为了让你知道它在做什么。
你还应该看看optimizing your InnoDB configuration,尤其是innodb_buffer_pool_size
(你能给它的内存越多越好)。在类似大小的表中,我在基于日期的字段(当然,我们立即索引)上遇到了类似的缓慢,将缓冲池大小从默认的 8 MB 增加到几 GB 产生了非常明显的差异。
如果您要从连接中涉及的任何表中删除许多行,您可以考虑同时运行 OPTIMIZE TABLE
。
【讨论】:
【参考方案2】:只是在黑暗中拍摄......
select A.*, b2, b5, b7, b8, b10, b13, b33
from A join B
on a1=b1
where b33 BETWEEN pos AND pos+999;
【讨论】:
【参考方案3】:我不是 MySQL(或任何东西!)专家,但有些事情我会考虑。首先,b33是均匀分布的吗?可能是因为有效地检索更多行而变慢了? 其次,您是否考虑过在单个查询中而不是 13500 中完成所有工作?比如:
select A.*, b2, b5, b7, b8, b10, b13, b33, (b33 - 1 DIV 1000) the_group
from A join B on a1=b1
第三,胡乱猜测,如果你的 MySQL 版本支持,先用 inlinew view 做过滤:
select A.*, b2, b5, b7, b8, b10, b13, b33
from A join (select b1,b2, b5, b7, b8, b10, b13, b33
from B b33 >= pos and b33 < pos+1000) B_NEW
on a1=b1 ;
第四(应该是第一个),做一个解释计划,并尝试比较快查询和慢查询,了解为什么查询很慢。
祝你好运!
【讨论】:
是的。 b33 分布均匀。这是解释的输出:*************************** 1. row *************************** id: 1 select_type: SIMPLE table: b type: range possible_keys: i1,id key: id key_len: 4 ref: NULL rows: 981 Extra: Using where *************************** 2. row *************************** id: 1 select_type: SIMPLE table: a type: eq_ref possible_keys: PRIMARY key: PRIMARY key_len: 4 ref: ja_StepUp.b.resid rows: 1 Extra: 2 rows in set (0.00 sec)
【参考方案4】:
您能告诉我们您在 B 上设置的索引吗? (感兴趣的是b33上的索引是如何定义的,是定义在单列还是多列):
SHOW INDEXES FROM B;
仅从 B 中选择时,您是否看到相同的速度下降?
即
select b2, b5, b7, b8, b10, b13, b33 from B where b33 >= pos and b33 < pos+1000;
您能否向我们展示SHOW CREATE TABLE
中涉及字段 b33 的部分(对允许为 NULL 感兴趣)
您是使用 MyISAM 还是 InnoDB 作为数据库引擎? (您可以在SHOW CREATE TABLE
的结果中看到这一点)。
【讨论】:
【参考方案5】:解释计划和索引似乎很好。
我建议你比较一下配置文件,看看时间到底去哪儿了:
SET profiling=1;
select A.*, b2, b5, b7, b8, b10, b13, b33 from A join B on a1=b1 where b33 >= 0 and b33 < 1000;
SHOW PROFILE;
select A.*, b2, b5, b7, b8, b10, b13, b33 from A join B on a1=b1 where b33 >= 1000000 and b33 < 1001000;
SHOW PROFILE;
SET profiling=0;
但我认为它可能会很慢,因为 600k 后的索引不再适合内存,并且进行了更多磁盘寻道
【讨论】:
【参考方案6】:您需要重构此查询!!!
这是您的旧查询:
select A.*, b2, b5, b7, b8, b10, b13, b33
from A join B on a1=b1 where b33 >= pos and b33 < pos+1000;
这是新的:
SELECT
AAA.*,b2,b5,b7,b8,b10,b13,b33
FROM
A AAA INNER JOIN
(
select
A.a1,b2,b5,b7,b8,b10,b13,b33
from
A INNER JOIN
(
SELECT
b1,b2,b5,b7,b8,b10,b13,b33
FROM B
WHERE
b33 >= pos and
b33 < pos+1000
) BB
ON A.a1=B.b1
) BBB
USING (a1)
;
警告
此重构查询的目标是使查询计划中的临时表尽可能小。事实上,子查询 BBB 在任何给定时间都不应超过 1000 行。
试试看!!!
【讨论】:
【参考方案7】:阿耶莎129p,
尝试将 b33 约束移动到连接子句中。听起来优化器只应用了一个 b33 约束 pre-join-set-creation。
select A.*, b2, b5, b7, b8, b10, b13, b33 from A join B
on a1=b1 and b33 >= pos and b33 < pos+1000;
这样优化器应该在尝试连接之前使用 b33 索引并将 B 行集减少到 1000。
【讨论】:
以上是关于Mysql select on indexed column slowdown on large tables的主要内容,如果未能解决你的问题,请参考以下文章
无法解析 ActiveRecord::StatementInvalid: Mysql2::Error: Duplicate key name 'index_users_on_email'