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的主要内容,如果未能解决你的问题,请参考以下文章

MySQL性能优化索引优化

如何在 3GB 表上优化 Mysql Select

Mysql索引优化

无法解析 ActiveRecord::StatementInvalid: Mysql2::Error: Duplicate key name 'index_users_on_email'

MySql查询select from 两个表与left join on的区别

MySql查询select from 两个表与left join on的区别