MySQL查询在大数据上痛苦地缓慢

时间:2012-10-14 16:36:47

标签: mysql pdo

我不是MySQL的天才,但我得到了它,我刚刚继承了一个非常大的表(600,000行和大约90列(请杀了我......))我有一个小的表,我已经创建了将其与类别表链接。

我正在尝试使用左连接查询所述表,因此我在一个对象中有两组数据,但它运行速度非常慢,而且我不够热,无法将其排序;我真的很感激一点指导和解释为什么它这么慢。

SELECT 
    `products`.`Product_number`,
    `products`.`Price`,
    `products`.`Previous_Price_1`,
    `products`.`Previous_Price_2`,
    `products`.`Product_number`,
    `products`.`AverageOverallRating`,
    `products`.`Name`,
    `products`.`Brand_description`
FROM `product_categories`
LEFT OUTER JOIN `products`
ON `products`.`product_id`= `product_categories`.`product_id`
WHERE COALESCE(product_categories.cat4, product_categories.cat3,
product_categories.cat2, product_categories.cat1) = '123456'
AND `product_categories`.`product_id` != 0

这两个表是MyISAM,产品表在Product_number和Brand_Description上有索引,product_categories表在所有列上都有唯一的索引;如果这些信息有任何帮助的话。

继承了这个系统之后,我需要尽快使用这个系统,然后才能正常工作,所以现在任何帮助都会赢得你的尊重!

[编辑] 以下是解释扩展的输出:

+----+-------------+--------------------+-------+---------------+------+---------+------+---------+----------+--------------------------+
| id | select_type | table              | type  | possible_keys | key  | key_len | ref  | rows    | filtered | Extra                    |
+----+-------------+--------------------+-------+---------------+------+---------+------+---------+----------+--------------------------+
|  1 | SIMPLE      | product_categories | index | NULL          | cat1 | 23      | NULL | 1224419 |   100.00 | Using where; Using index |
|  1 | SIMPLE      | products           | ALL   | Product_id    | NULL | NULL    | NULL |  512376 |   100.00 |                          |
+----+-------------+--------------------+-------+---------------+------+---------+------+---------+----------+--------------------------+

3 个答案:

答案 0 :(得分:3)

优化表

要建立基线,我建议首先在两个表上运行OPTIMIZE TABLE命令。请注意,这可能需要一些时间。来自docs

  如果删除了大部分内容,则应使用

OPTIMIZE TABLE   表或者如果您对具有可变长度的表进行了许多更改   行(具有VARCHAR, VARBINARY, BLOBTEXT列的表)。   已删除的行将保留在链接列表中,并随后INSERT   操作重用旧行位置。您可以使用OPTIMIZE TABLE   回收未使用的空间并对数据文件进行碎片整理。后   对表格进行了大量更改,此声明也可能会有所改进   使用该表的语句的性能,有时是显着的。

     

[...]

     

对于MyISAM表,OPTIMIZE TABLE的工作原理如下:

     
      
  1. 如果表已删除或拆分行,请修复该表。

  2.   
  3. 如果未对索引页面进行排序,请对它们进行排序。

  4.   
  5. 如果表的统计信息不是最新的(并且无法通过对索引进行排序来完成修复),请更新它们。

  6.   

索引

如果不考虑空间和索引管理,您可以尝试在

上添加composite index
product_categories.cat4, product_categories.cat3, product_categories.cat2, product_categories.cat1

如果您在查询中使用这些列的最左侧子集经常,则会建议您这样做。查询计划表明它可以使用cat1的{​​{1}}索引。这很可能只包含product_categories列。通过将所有四个类别列添加到索引,它可以更有效地寻找所需的行。来自docs

  

MySQL可以对测试所有内容的查询使用多列索引   索引中的列,或仅测试第一列的查询,   前两列,前三列,依此类推。如果你指定   索引定义中的列顺序正确,单个   复合索引可以在同一个上加速多种查询   表

结构

此外,鉴于您的表格 90列,您还应该知道a wider table can lead to slower query performance。您可能希望将您的表Vertically Partitioning考虑​​到多个表中:

  

拥有太多列可能会使您的记录大小膨胀   导致更多的内存块被读入和读出内存   更高的I / O.这可能会影响性能。解决这个问题的一种方法是   将表拆分为更小的更独立的表   基数比原来的要大。现在应该可以做得更好   阻塞因子(如上所述)意味着更少的I / O和更快   性能即可。像这样拆开桌子的过程是一个   称为垂直分区

答案 1 :(得分:1)

您的查询的含义似乎是“找到所有类别为'123456'的产品。”这是对的吗?

COALESCE是在WHERE语句中使用的非常昂贵的函数,因为它在索引恶意NULL值上运行。您的解释结果显示您的查询对product_categories表的选择性不高。在MySQL中,如果要利用索引来快速查询,则需要完全避免WHERE语句中的函数。

其他人说有关90列表有害的事情也是如此。但你坚持下去,所以我们只是处理它。

我们可以重写您的查询以摆脱基于函数的WHERE吗?我们来试试吧。

SELECT  /* some columns from the products table */
  FROM products
 WHERE product_id IN 
 (
     SELECT DISTINCT product_id 
                FROM product_categories
               WHERE product_id <> 0
                 AND (   cat1='123456'
                      OR cat2='123456'
                      OR cat3='123456'
                      OR cat4='123456')
 )

为了快速工作,您需要在四个cat列上创建单独的索引。复合唯一索引(“在所有列上组合”)不会对您有所帮助。它仍然可能不太好。

更好的解决方案可能是FULLTEXT在BOOLEAN模式下搜索。您正在使用MyISAM访问方法,因此这是可能的。这绝对值得一试。它确实非常快。

SELECT  /* some columns from the products table */
  FROM products
 WHERE product_id IN 
 (
     SELECT product_id 
       FROM product_categories
      WHERE MATCH(cat1,cat2,cat3,cat4) 
            AGAINST('123456' IN BOOLEAN MODE)
        AND product_id <> 0

 )

为了使其快速工作,您需要创建一个类似的FULLTEXT索引。

 CREATE FULLTEXT INDEX cat_lookup 
                    ON product_categories (cat1, cat2, cat3, cat4)

请注意,这些建议的查询都不会产生与COALESCE查询完全相同的结果。设置COALESCE查询的方式,某些组合与匹配这些查询的组合不匹配。例如。

    cat1     cat2     cat3     cat4   
  123451   123453   123455   123456      matches your and my queries
  123456   123455   123454   123452      matches my queries but not yours

但是我的查询很可能会生成一个有用的产品列表,即使它还有更多的产品。

您可以通过使用product_categories上的内部查询来调试这些内容。

答案 2 :(得分:0)

有些奇怪的事。表格product_categories确实有product_id列吗? fromwhere条款不应该是这样的:

FROM `product_categories` pc 
LEFT OUTER JOIN `products` p ON p.category_id = pc.id
WHERE 
    COALESCE(product_categories.cat4, product_categories.cat3,product_categories.cat2, product_categories.cat1) = '123456'
    AND pc.id != 0