【问题标题】:Slow query SELECT ... GROUP BY on two tables fields` (MySQL/InnoDB)两个表字段上的慢查询 SELECT ... GROUP BY (MySQL/InnoDB)
【发布时间】:2011-07-18 11:53:11
【问题描述】:

我有一个选择订单的查询,对于每个订单,我需要获取其中包含的产品列表以及每个产品的汇总数量。

目前速度很慢(我的设置需要 10 秒),我需要加快速度。

更新:不能切换到 MyISAM,我需要交易

目前的查询是这样的:

SELECT `Order`.*, `Product`.`id`, `Product`.`msrp`, SUM(`OrderItem`.`quantity`) AS sum
FROM `orders` AS `Order` 
LEFT JOIN `order_items` AS `OrderItem` ON (`OrderItem`.`order_id` = `Order`.`id`)
LEFT JOIN `product_variations` AS `ProductVariation` ON (`OrderItem`.`product_variation_id` = `ProductVariation`.`id`)
LEFT JOIN `products` AS `Product` ON (`ProductVariation`.`product_id` = `Product`.`id`)
WHERE 1 = 1
GROUP BY `Order`.`id`, `Product`.`id`
ORDER BY `Order`.`created` DESC
LIMIT 20;

解释:

架构(截断,只剩下必填字段):

CREATE TABLE `orders` (
  `id` int(11) NOT NULL auto_increment,
  `created` timestamp NOT NULL default CURRENT_TIMESTAMP,
  `customer_comments` text NOT NULL,
  PRIMARY KEY  (`id`),
  KEY `created` (`created`),
  KEY `id_created` (`id`,`created`),
) ENGINE=InnoDB 

CREATE TABLE `order_items` (
  `id` int(11) NOT NULL auto_increment,
  `order_id` int(11) NOT NULL,
  `product_variation_id` int(11) NOT NULL,
  `type` enum('First','Second') default NULL,
  `quantity` int(11) NOT NULL,
  PRIMARY KEY  (`id`),
  UNIQUE KEY `item_UNIQUE` (`order_id`,`product_variation_id`,`type`),
  KEY `fk_order_items_product_variations1` (`product_variation_id`),
  CONSTRAINT `fk_order_items_orders1` FOREIGN KEY (`order_id`) REFERENCES `orders` (`id`) ON DELETE NO ACTION ON UPDATE NO ACTION,
  CONSTRAINT `fk_order_items_product_variations1` FOREIGN KEY (`product_variation_id`) REFERENCES `product_variations` (`id`) ON DELETE NO ACTION ON UPDATE NO ACTION
) ENGINE=InnoDB

CREATE TABLE `product_variations` (
  `id` int(11) NOT NULL auto_increment,
  `product_id` int(11) NOT NULL,
  PRIMARY KEY  (`id`),
  CONSTRAINT `fk_product_variations_products1` FOREIGN KEY (`product_id`) REFERENCES `products` (`id`) ON DELETE NO ACTION ON UPDATE NO ACTION,
) ENGINE=InnoDB 

CREATE TABLE `products` (
  `id` int(11) NOT NULL auto_increment,
  `msrp` decimal(5,2) NOT NULL,
  PRIMARY KEY  (`id`),
) ENGINE=InnoDB

服务器是 MySQL 5.0.77,表是 InnoDB。 orders 大约有 75k 条记录,order_items 160k 条记录,product_variations 140k 条记录,product - 300 条记录。

感谢任何帮助。

【问题讨论】:

    标签: mysql performance select group-by


    【解决方案1】:

    我会尝试的第一件事是将SUM 替换为预先计算的product_sum 表。众所周知,InnoDB 的聚合函数速度较慢(尤其是 75k+ 行!)。您真的不希望每次查询都计算这个总和(除非数据经常更改)。

    在插入/更新订单后使用重新计算 product_sum 表的触发器可能会有所帮助.. 但不清楚此查询的上下文是什么(它是由最终用户运行的吗?它是网络吗?请求或报告?等)。

    另一种选择是将引擎切换到 MyISAM(使用聚合时速度更快),但是一个巨大的负面影响是您会丢失事务和外键约束。

    【讨论】:

    • 我将预计算总和和其他聚合作为最后的手段。我的理解是,如果 RDBMS 擅长某件事——那就是过滤、分组和聚合。我当然可以预先计算所有的聚合,为什么还要使用 RDBMS?
    • 关于上下文-它是Web界面分页查询; (实际上是其中的一部分 - 我需要为每个 order 再总结一次 - 不知道该怎么做,因为 WITH ROLLUP 不适用于此查询)。
    • 这里的问题不是RDBMS,而是表引擎。切换到 MyISAM 只是为了测试聚合是否确实是瓶颈。 InnoDB 在聚合时会读取每一行,而 MyISAM 不会。所以不幸的是,如果你想要 InnoDB 的事务/约束功能,你必须自己缓存聚合。
    【解决方案2】:

    尽管您的查询看起来结构良好且连接在一起,但记录量可能是问题所在。你有分组依据等。我会添加一个子句......

    SELECT STAIGHT_JOIN ...查询的其余部分。

    STRAIGHT_JOIN 告诉引擎按照您声明的顺序执行此操作,而不是尝试将较小的表优化为主表,然后以某种相反的意图顺序加入。我已经看到 STRAIGHT_JOIN 在其他几个领域的出色表现......也许它也可以帮助你。

    【讨论】:

    • 不幸的是,不在这个。试过了 - 没有显着差异。
    猜你喜欢
    • 1970-01-01
    • 2018-09-07
    • 1970-01-01
    • 1970-01-01
    • 2011-01-29
    • 2021-02-13
    • 1970-01-01
    • 2013-05-30
    • 1970-01-01
    相关资源
    最近更新 更多