【问题标题】:Why is limit 0,1 slower than limit 0, 17为什么限制 0,1 比限制 0, 17 慢
【发布时间】:2021-09-25 14:37:53
【问题描述】:

我正在尝试分析为什么LIMIT 0,1 的以下查询比LIMIT 0,100

我已添加 SQL_NO_CACHE 用于测试目的。

查询:

 SELECT 
  SQL_NO_CACHE  SQL_CALC_FOUND_ROWS wp_posts.*, 
  low_stock_amount_meta.meta_value AS low_stock_amount
FROM 
  wp_posts 
  LEFT JOIN wp_wc_product_meta_lookup wc_product_meta_lookup ON wp_posts.ID = wc_product_meta_lookup.product_id 
  LEFT JOIN wp_postmeta AS low_stock_amount_meta ON wp_posts.ID = low_stock_amount_meta.post_id 
  AND low_stock_amount_meta.meta_key = '_low_stock_amount' 
WHERE 
  1 = 1 
  AND wp_posts.post_type IN ('product', 'product_variation') 
  AND (
    (wp_posts.post_status = 'publish')
  ) 
  AND wc_product_meta_lookup.stock_quantity IS NOT NULL 
  AND wc_product_meta_lookup.stock_status IN('instock', 'outofstock') 
  AND (
    (
      low_stock_amount_meta.meta_value > '' 
      AND wc_product_meta_lookup.stock_quantity <= CAST(
        low_stock_amount_meta.meta_value AS SIGNED
      )
    ) 
    OR (
      (
        low_stock_amount_meta.meta_value IS NULL 
        OR low_stock_amount_meta.meta_value <= ''
      ) 
      AND wc_product_meta_lookup.stock_quantity <= 2
    )
  ) 

ORDER BY 
  wp_posts.ID DESC 
LIMIT 
  0, 1

解释显示完全相同的输出

1   SIMPLE  wp_posts    index   PRIMARY,type_status_date    PRIMARY 8   NULL    27071   Using where
1   SIMPLE  low_stock_amount_meta   ref post_id,meta_key    meta_key    767 const   1   Using where
1   SIMPLE  wc_product_meta_lookup  eq_ref  PRIMARY,stock_status,stock_quantity,product_id  PRIMARY 8   woocommerce-admin.wp_posts.ID   1   Using where

LIMIT 0,1的平均查询时间为350ms

LIMIT 0,100的平均查询时间为7ms

LIMIT 0,17开始查询性能变快

按照question 中的建议,我在 order by 子句中添加了另一列,但这会在解释输出中触发 Using filesort

Order by wp_posts.post_date, wp_posts.ID desc

1   SIMPLE  wp_posts    ALL PRIMARY,type_status_date    NULL    NULL    NULL    27071   Using where; Using filesort
1   SIMPLE  low_stock_amount_meta   ref post_id,meta_key    meta_key    767 const   1   Using where
1   SIMPLE  wc_product_meta_lookup  eq_ref  PRIMARY,stock_status,stock_quantity,product_id  PRIMARY 8   woocommerce-admin.wp_posts.ID   1   Using where

有没有办法在不改变索引的情况下解决这个问题?为什么会这样?

有趣的是查询时间从LIMIT 0,17 开始有所改善。我不知道为什么 17 在这里是一个神奇的数字。

更新 1:我刚刚尝试添加 FORCE INDEX(PRIMARY),现在 LIMIT 0,100LIMIT 0,1 smh 具有相同的性能

【问题讨论】:

  • 通常第二次最好。 第一次时间包括从磁盘获取内容。
  • @RickJames 但我有 SQL_NO_CACHE,区别是 350 毫秒和 7 毫秒,即使是第一次运行 LIMIT 0,17 也很快。
  • 缓冲池也是缓存。在新系统上,直到第一次查询之后才加载任何帖子和 postmeta。因此,如果第一次查询测试花费了几秒钟,“平均值”将异常高,即使其余的非常低。 (而且,是的,最好避免“查询缓存”。)
  • @RickJames 我明白你在说什么,但它仍然没有解释为什么 LIMIT 0, 17 比 0,1 快得多的问题,即使对于第一个查询也是如此。我什至尝试重新启动系统以使系统变冷。
  • 请再次确认您的执行计划实际上是相同的,因为只有在它们不同时才会发生这种情况。另外,您提到您添加了“强制索引(主)”(究竟在哪里?),那么查询是同样快还是同样慢?如果强制执行计划中显示所有 3 个索引会发生什么?

标签: mysql sql wordpress query-optimization limit


【解决方案1】:

wp_postmeta 有草率的索引;这会减慢大多数涉及它的查询。

哦。 Jones 和我创建了一个WordPress plugin 来改进 postmeta 的索引。我们检测各种东西,例如 InnoDB 存储引擎的梭子鱼版本和其他 MySQL 奥秘的存在,然后做正确的事情。

可能加快所有三个平均值。 EXPLAINs很有可能会改。

【讨论】:

  • 谢谢,Rick,但我不确定插件的作用。我的wp_postmeta 表已经有 meta_id、post_id、meta_key 和 meta_value 的索引。看起来插件向我已经拥有的表添加了相同的索引。
  • 单列索引与“复合”(多列)索引相同。
  • 瑞克·詹姆斯 // 啊!我明白你想做什么。不幸的是,这对我没有帮助。
  • EXPLAINs 改变了吗?
  • Rick James // 不,它没有。正如您在我的原始帖子中看到的,EXPLAIN 的输出非常好,没有任何警告标志,例如 using temporaryusing filesort
【解决方案2】:

分析这个查询。我承认我不明白从 LIMIT 1 到 LIMIT 17 的性能变化。不过,对于您商店的客户(或经理)来说,问题是 LIMIT 1 的速度很慢。所以让我们解决这个问题。

question you linked 用于 postgreSQL,而不是 MySQL。 postgreSQL 有一种比 MySQL 更复杂的方式来处理 ORDER BY ... LIMIT 1。并且,该问题的解决方案是为所需的查找添加适当的复合索引。

在我看来,您查询的目的是找到最大的 wp_posts.ID 的低库存或缺货 WooCommerce 产品

wp_wc_product_meta_lookup 表的 LEFT JOIN 应该并且现在很简单:提到的 ON 条件列是它的主键。该表基本上是 WooCommerce 的数值的物化视图,例如存储在 wp_postmeta 中的 stock_quantity。 wp_postmeta 中的数值无法被索引,因为该表将它们存储为文本字符串。是的。我知道。

wp_postswp_postmeta 之间的 LEFT JOIN 遵循非常常见的 ON 条件模式 ON posts.ID = meta.post_id AND meta.meta_key = 'constant'。该 ON 条件因 WordPress 标准索引的支持不佳而臭名昭著。 Rick 和我的Index WP MySQL For Speed plugin 的全部目的或多或少是在wp_postmeta 中提供良好的复合索引来解决这个问题。

怎么会? This is the DDL 它运行以添加索引。为此目的最重要的几行:((还有更多内容,请阅读链接的文章。)

ALTER TABLE wp_postmeta ADD PRIMARY KEY (post_id, meta_key, meta_id);
ALTER TABLE wp_postmeta ADD KEY meta_key (meta_key, post_id);

这两个索引支持查询中的 ON 条件模式。我很确定将这些键添加到 postmeta 将使您的查询更可预测且性能更快。

如果ORDER BY post.ID DESC 是一个非常常见的用例,则可以为此添加一个索引。

您可以尝试重构查询(如果您可以控制其来源)以推迟从wp_posts 表中检索详细信息。像这样。

SELECT wp_posts.*, postid.low_stock_amount
 FROM (
   wp_posts.ID, low_stock_amount_meta.meta_value AS low_stock_amount
FROM 
  wp_posts 
  LEFT JOIN wp_wc_product_meta_lookup wc_product_meta_lookup ON wp_posts.ID = wc_product_meta_lookup.product_id 
  LEFT JOIN wp_postmeta AS low_stock_amount_meta ON wp_posts.ID = low_stock_amount_meta.post_id 
  AND low_stock_amount_meta.meta_key = '_low_stock_amount' 
WHERE 
  1 = 1 
  AND wp_posts.post_type IN ('product', 'product_variation') 
  AND (
    (wp_posts.post_status = 'publish')
  ) 
  AND wc_product_meta_lookup.stock_quantity IS NOT NULL 
  AND wc_product_meta_lookup.stock_status IN('instock', 'outofstock') 
  AND (
    (
      low_stock_amount_meta.meta_value > '' 
      AND wc_product_meta_lookup.stock_quantity <= CAST(
        low_stock_amount_meta.meta_value AS SIGNED
      )
    ) 
    OR (
      (
        low_stock_amount_meta.meta_value IS NULL 
        OR low_stock_amount_meta.meta_value <= ''
      ) 
      AND wc_product_meta_lookup.stock_quantity <= 2
    )
  ) 

ORDER BY 
  wp_posts.ID DESC 
LIMIT 
  0, 1
) postid
LEFT JOIN wp_posts ON wp_posts.ID = postid.ID

此重构使您的复杂查询仅对 wp_posts.ID 值进行排序,然后在获得适当的值后检索帖子数据。许多 WordPress 核心代码都在做类似的事情:在一个查询中检索帖子 ID 值列表,然后在第二个查询中检索帖子数据。

顺便说一句,MySQL 8 忽略了SQL_NO_CACHE

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-02-14
    • 2011-03-12
    • 2020-11-26
    • 2014-11-29
    • 2021-07-19
    • 2020-01-20
    • 2017-01-06
    相关资源
    最近更新 更多