【问题标题】:Slow MySQL Query Performance缓慢的 MySQL 查询性能
【发布时间】:2016-03-09 03:51:56
【问题描述】:

请建议我如何在 MySQL 中加快此查询的性能。它运行得很慢。

查询:

SELECT *
FROM product, search_attribute, search_attribute_values 
WHERE 
product.categoryid = 4800 AND product.productid = search_attribute.productid  
AND search_attribute.valueid = search_attribute_values.valueid 
GROUP BY search_attribute.valueid

查询说明:

+----+-------------+-------------------------+--------+-----------------------------+---------+---------+-------------------------------------+----------+---------------------------------+
| id | select_type | table                   | type   | possible_keys               | key     | key_len | ref                                 | rows     | Extra                           |
+----+-------------+-------------------------+--------+-----------------------------+---------+---------+-------------------------------------+----------+---------------------------------+
|  1 | SIMPLE      | search_attribute        | ALL    | PRIMARY,attributeid_valueid | NULL    | NULL    | NULL                                | 79801024 | Using temporary; Using filesort |
|  1 | SIMPLE      | search_attribute_values | eq_ref | PRIMARY                     | PRIMARY | 4       | microcad.search_attribute.valueid   |        1 |                                 |
|  1 | SIMPLE      | product                 | eq_ref | PRIMARY,product_categoryID  | PRIMARY | 4       | microcad.search_attribute.productid |        1 | Using where                     |
+----+-------------+-------------------------+--------+-----------------------------+---------+---------+-------------------------------------+----------+---------------------------------+

架构:

--
-- Table structure for table `attributenames`
--

DROP TABLE IF EXISTS `attributenames`;
/*!40101 SET @saved_cs_client     = @@character_set_client */;
/*!40101 SET character_set_client = utf8 */;
CREATE TABLE `attributenames` (
  `attributeid` bigint(20) NOT NULL DEFAULT '0',
  `name` varchar(110) NOT NULL DEFAULT '',
  `localeid` int(11) NOT NULL DEFAULT '0',
  KEY `attributenames_attributeID` (`attributeid`),
  KEY `attributenames_localeID` (`localeid`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1;
/*!40101 SET character_set_client = @saved_cs_client */;

--
-- Table structure for table `product`
--

DROP TABLE IF EXISTS `product`;
/*!40101 SET @saved_cs_client     = @@character_set_client */;
/*!40101 SET character_set_client = utf8 */;
CREATE TABLE `product` (
  `productid` int(11) NOT NULL DEFAULT '0',
  `manufacturerid` int(11) NOT NULL DEFAULT '0',
  `isactive` tinyint(1) NOT NULL DEFAULT '1',
  `mfgpartno` varchar(70) NOT NULL DEFAULT '',
  `categoryid` int(11) NOT NULL DEFAULT '0',
  `isaccessory` tinyint(1) NOT NULL DEFAULT '0',
  `equivalency` double NOT NULL DEFAULT '0',
  `creationdate` datetime NOT NULL DEFAULT '0000-00-00 00:00:00',
  `modifieddate` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  `lastupdated` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
  PRIMARY KEY (`productid`),
  KEY `product_manufacturerID` (`manufacturerid`),
  KEY `product_categoryID` (`categoryid`),
  KEY `product_mfgPartNo` (`mfgpartno`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1;
/*!40101 SET character_set_client = @saved_cs_client */;

--
-- Table structure for table `search_attribute`
--

DROP TABLE IF EXISTS `search_attribute`;
/*!40101 SET @saved_cs_client     = @@character_set_client */;
/*!40101 SET character_set_client = utf8 */;
CREATE TABLE `search_attribute` (
  `productid` int(11) NOT NULL DEFAULT '0',
  `attributeid` bigint(20) NOT NULL DEFAULT '0',
  `valueid` int(11) NOT NULL DEFAULT '0',
  `localeid` int(11) NOT NULL DEFAULT '0',
  `setnumber` tinyint(2) NOT NULL DEFAULT '0',
  `isactive` tinyint(1) NOT NULL DEFAULT '1',
  PRIMARY KEY (`productid`,`localeid`,`attributeid`,`setnumber`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1;
/*!40101 SET character_set_client = @saved_cs_client */;

--
-- Table structure for table `search_attribute_values`
--

DROP TABLE IF EXISTS `search_attribute_values`;
/*!40101 SET @saved_cs_client     = @@character_set_client */;
/*!40101 SET character_set_client = utf8 */;
CREATE TABLE `search_attribute_values` (
  `valueid` int(11) NOT NULL DEFAULT '0',
  `value` varchar(255) NOT NULL DEFAULT '',
  `absolutevalue` double NOT NULL DEFAULT '0',
  `unitid` int(11) NOT NULL DEFAULT '0',
  `isabsolute` tinyint(1) NOT NULL DEFAULT '0',
  PRIMARY KEY (`valueid`),
  KEY `search_attrval_value` (`value`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1;
/*!40101 SET character_set_client = @saved_cs_client */;

每张表的记录数:

search_attribute 为 72,000,000, search_attribute_values 是 350,000, 产品为 4,000,000

【问题讨论】:

  • 将索引添加到您在 where 子句中使用的字段。也可以考虑使用连接。

标签: mysql performance optimization


【解决方案1】:

您的索引应该没问题。 product 表在 categoryid 上有一个索引,然后它应该从该索引连接到 search_attribute,它在多个列上有一个覆盖索引,第一个是 productid(应该使用)。然后它应该使用作为主键的 valueid 加入到 search_attribute_values。

但是,由于某种原因,MySQL 似乎决定对 search_attribute 进行非键控读取,返回大量行,然后尝试将其他行加入其中。可能是因为 GROUP BY(这可能会为所有其他返回的列返回奇怪的值)。

我会尝试的第一件事是强制 MySQL 重建索引统计信息(使用ANALYZE TABLE)。然后它可能会有用地使用它们。

尝试使用 STRAIGHT_JOIN 失败:-

SELECT *
FROM product 
STRAIGHT_JOIN search_attribute ON product.productid = search_attribute.productid
STRAIGHT_JOIN search_attribute_values ON search_attribute.valueid = search_attribute_values.valueid 
WHERE product.categoryid = 4800 
GROUP BY search_attribute.valueid

但是,您实际上想要返回什么值?例如,您的查询将为每个 search_attribute valueid 返回 1 个类别 id 为 4800 的产品。返回哪个产品没有定义,同样假设几个搜索属性可以具有相同的 valueid,那么选择其中一个也没有定义。

虽然这不会出错并且在 MySQL 中确实会返回某些内容,但在大多数 SQL 风格中都会出错。

【讨论】:

【解决方案2】:

您可以通过在用于从表中获取数据的所有列(即您在 where 子句中提到的列)上添加索引来提高查询的性能。

所以你已经创建了复合主键,但是你正在使用 search_attribute.valueid 进行查询,因此必须添加另一个索引。

ALTER TABLE `search_attribute` ADD INDEX `valueid ` (`valueid `)
ALTER TABLE `search_attribute` ADD INDEX `productid ` (`productid `)

这很可能会提高性能。

【讨论】:

    【解决方案3】:

    请使用JOIN...ON语法:

    SELECT  *
        FROM  product AS p
        JOIN  search_attribute AS sa ON p.productid = sa.productid
        JOIN  search_attribute_values AS sav ON sa.valueid = sav.valueid
        WHERE  p.categoryid = 4800
        GROUP BY  sa.valueid 
    

    您的GROUP BY 无效,因为有许多字段(在* 中)既不包含在GROUP BY 中,也不包含在聚合中(COUNTSUM 等)。

    InnoDB 会更好。

    这会欺骗它使用 categoryid 上的索引,而不是从 72M 行的表开始:

    SELECT  *
        FROM  
          ( SELECT  *
                FROM  product AS p
                JOIN  search_attribute AS sa ON p.productid = sa.productid
                JOIN  search_attribute_values AS sav ON sa.valueid = sav.valueid
                WHERE  p.categoryid = 4800 
          ) x
        GROUP BY  x.valueid 
    

    * 仍然存在问题。

    【讨论】:

      猜你喜欢
      • 2016-05-10
      • 1970-01-01
      • 1970-01-01
      • 2015-04-11
      • 1970-01-01
      • 1970-01-01
      • 2018-09-07
      • 2022-01-09
      • 1970-01-01
      相关资源
      最近更新 更多