【问题标题】:Mysql select on indexded column slowdown on large tablesMysql select on indexed 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 是一场噩梦。
  • 我想这是慢速解释的输出。还有那个快的?有相同的输出吗?

标签: mysql select join indexing slowdown


【解决方案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。

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 2015-07-21
              • 2012-07-31
              • 2022-12-01
              • 2020-06-12
              • 2018-07-13
              • 1970-01-01
              • 2011-07-06
              相关资源
              最近更新 更多