【问题标题】:Improve query performance with 5 join使用 5 连接提高查询性能
【发布时间】:2017-01-27 10:00:52
【问题描述】:

使用本地 java 程序,我可以访问 vps 上的数据库,其中涉及的表平均有大约 1,500 条记录。

不幸的是,尽管记录不多,但我在提取 10 秒左右时遇到了性能问题。

查询如下:

SELECT  DISTINCT P.advanced_stock_management,
        PA.id_product, PA.reference, PL.name,
        round(P.wholesale_price,2),
        round((P.price + P.price * 22 / 100), 2),
        round(SP.reduction,2),
        SA.quantity, PQ.is_true
    FROM  db.ps_product AS P
    INNER JOIN  db.ps_product_attribute AS PA
          ON P.id_product=PA.id_product
    INNER JOIN  db.ps_product_lang AS PL 
          ON PA.id_product=PL.id_product
    INNER JOIN  db.ps_stock_available AS SA
          ON SA.id_product_attribute=PA.id_product_attribute
    LEFT OUTER JOIN  db.ps_product_quantity_real AS PQ
          ON PA.id_product=PQ.id_product
         AND  PA.reference=PQ.reference
    LEFT OUTER JOIN  db.ps_specific_price AS SP
          ON PA.id_product=SP.id_product
    WHERE  P.active = 1;

如何改进查询结构并提高性能?

提前致谢。

【问题讨论】:

  • 你需要DISTINCT吗?
  • VPS?或者你的意思是VPN?确定不是网速慢?
  • 你能告诉我们有关表的定义和索引吗?在如此小的表上,如此长的查询时间可能表明索引不足或缺失。
  • 几年前,当我试图将所有教科书都用于这样的任务时,我也遇到了同样的问题。事实证明,拥有 100 个小查询要好得多。因此,如果可以的话,只需将 JOIN 查询分解为一系列较小的单独查询
  • 请发布架构(包括索引)和说明 - 否则,我们只是在猜测。

标签: mysql sql performance innodb database-performance


【解决方案1】:

您应该在以下列上有适当的索引:

table_name (column_name)
ps_product (id_product)
ps_product_attribute (id_product)
ps_product_attribute (reference) 
ps_product_attribute (id_product_attribute)
ps_product_lang (id_product)
ps_stock_available (id_product_attribute)
ps_product_quantity_real (id_product)
ps_product_quantity_real (reference)
ps_specific_price (id_product)

【讨论】:

  • 查看 Radu 的评论——使用复合索引。
  • ps_product (id_product) 不会被使用。无论如何它可能是PRIMARY KEY,这是一个索引。
  • @RickJames 我认为这里正在使用它on ps_product.id_product = ps_product_attribute.id_product。假设作者可能知道它,我还没有编写用于添加索引的完整代码。我刚刚描述了他需要创建索引的列。
【解决方案2】:

通过使用适当的缩进重写查询:

SELECT DISTINCT 
    product.advanced_stock_management, 
    attribute.id_product, 
    attribute.reference, 
    lang.name, 
    round(product.wholesale_price,2), 
    round((product.price + product.price * 22 / 100),2), 
    round(price.reduction,2), 
    availablity.quantity, 
    quantity.is_true
FROM db.ps_product AS product
    INNER JOIN db.ps_product_attribute AS attribute 
        ON product.id_product = attribute.id_product
            INNER JOIN db.ps_product_lang AS lang 
                ON lang.id_product = attribute.id_product
            INNER JOIN db.ps_stock_available AS availablity 
                ON availablity.id_product_attribute = attribute.id_product_attribute
            LEFT OUTER JOIN db.ps_product_quantity_real AS quantity 
                ON attribute.id_product = quantity.id_product 
                AND attribute.reference = quantity.reference
            LEFT OUTER JOIN db.ps_specific_price AS price 
                ON price.id_product = attribute.id_product
WHERE product.active = 1;

我们可以看到,中心表实际上是ps_product_attribute。让我们把它作为查询的开始:

SELECT DISTINCT 
    product.advanced_stock_management, 
    attribute.id_product, 
    attribute.reference, 
    lang.name, 
    round(product.wholesale_price,2), 
    round((product.price + product.price * 22 / 100),2), 
    round(price.reduction,2), 
    availablity.quantity, 
    quantity.is_true
FROM db.ps_product_attribute AS attribute
    INNER JOIN db.ps_product AS product
        ON attribute.id_product = product.id_product
    INNER JOIN db.ps_product_lang AS lang 
        ON attribute.id_product = lang.id_product 
    INNER JOIN db.ps_stock_available AS availablity 
        ON attribute.id_product_attribute = availablity.id_product_attribute
    LEFT OUTER JOIN db.ps_product_quantity_real AS quantity 
        ON attribute.id_product = quantity.id_product 
        AND attribute.reference = quantity.reference
    LEFT OUTER JOIN db.ps_specific_price AS price 
        ON attribute.id_product = price.id_product
WHERE product.active = 1;

现在查询看起来很不错。
您在此查询中的每个字段上都有索引吗?如果没有,你应该!

ALTER TABLE `ps_product_attribute` ADD INDEX `id_product` (`id_product`)
ALTER TABLE `ps_product_attribute` ADD INDEX `reference` (`reference`)
ALTER TABLE `ps_product` ADD INDEX `id_product` (`id_product`)
ALTER TABLE `ps_product_lang` ADD INDEX `id_product` (`id_product`)
ALTER TABLE `ps_stock_available` ADD INDEX `id_product_attribute` (`id_product_attribute`)
ALTER TABLE `ps_product_quantity_real` ADD INDEX `id_product` (`id_product`)
ALTER TABLE `ps_product_quantity_real` ADD INDEX `reference` (`reference`)
ALTER TABLE `ps_specific_price` ADD INDEX `id_product` (`id_product`)

使用此数据库大小,查询应在 1 秒内运行。

【讨论】:

  • 您是如何衡量性能以表明查询现在将在不到 1 秒的时间内运行的?另外,您为什么选择在同一张表上创建两个索引,每个索引都有一列?而不是创建单个复合索引?
  • 不,优化器将从带有WHERE 子句的唯一表ps_product 开始。
【解决方案3】:

你有两个选择

  • 在内存中预取 1500 条记录,然后使用记录匹配连接(无数据库)。预取可提高访问数据库记录的速度并消除网络延迟,但记录匹配会很慢且令人厌烦。

  • 将 apache spark 与本地集群一起使用(大数据连接)。我个人在 9 毫秒内执行了 100 万 * 2 次连接。这必须仅使用内存中的RDD 来实现。

【讨论】:

  • 预取需要更多的网络时间。添加索引就是答案。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-10-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多