【问题标题】:Optimizing SQL for applying conditions to multiple rows优化 SQL 以将条件应用于多行
【发布时间】:2021-04-25 11:01:34
【问题描述】:

发现问题:

仅通过删除 ORDER BY 子句,我就能将响应时间从 1.1 秒显着减少到不到 0.01 秒。

无论如何,我在 PHP 中对日期进行了 USORT,因为后处理会按匹配类型对帖子重新排序,因此 ORDER BY 子句是不必要的。

Larry Lustig 描述的查询对于整数来说非常快。正是字符串日期的排序导致了严重的性能问题。

希望此服务器作为一个很好的例子来说明如何将条件应用于多行以及为什么要注意涉及字符串的数据库操作。

原始问题(为清晰而编辑):

我正在使用关键字词干进行索引搜索。我希望对其进行更多优化。

Larry Lustig 的回答在如何创建查询以方便扩展方面帮助了我很多:

SQL for applying conditions to multiple rows in a join

关于如何优化此示例查询以更快运行的任何建议?我猜可能有一种方法可以对所有“s{x).post_id = p.ID”条件进行一次连接。

SELECT p.ID, p.post_title, LEFT ( p.post_content, 800 ), p.post_date 
FROM wp_posts AS p
WHERE p.post_status = 'publish' AND ( ( ( 
    ( EXISTS ( SELECT s0.post_id FROM wp_rama_search_index AS s0 
        WHERE s0.post_id = p.ID AND s0.hash = -617801035 ) )
) OR ( 
    ( EXISTS ( SELECT s1.post_id FROM wp_rama_search_index AS s1 
            WHERE s1.post_id = p.ID AND s1.hash = 1805184399 ) 
        AND EXISTS ( SELECT s2.post_id FROM wp_rama_search_index AS s2 
            WHERE s2.post_id = p.ID AND s2.hash = 1823159221 ) 
        AND EXISTS ( SELECT s3.post_id FROM wp_rama_search_index AS s3 
            WHERE s3.post_id = p.ID AND s3.hash = 1692658528 ) ) 
) OR ( 
    ( EXISTS ( SELECT s4.post_id FROM wp_rama_search_index AS s4 
            WHERE s4.post_id = p.ID AND s4.hash = 332583789 ) ) 
) OR ( 
    ( EXISTS ( SELECT s5.post_id FROM wp_rama_search_index AS s5 
            WHERE s5.post_id = p.ID AND s5.hash = 623525713 ) ) 
) OR ( 
    ( EXISTS ( SELECT s6.post_id FROM wp_rama_search_index AS s6 
            WHERE s6.post_id = p.ID AND s6.hash = -2064050708 ) 
        AND EXISTS ( SELECT s7.post_id FROM wp_rama_search_index AS s7 
            WHERE s7.post_id = p.ID AND s7.hash = 1692658528 ) ) 
) OR ( 
    ( EXISTS ( SELECT s8.post_id FROM wp_rama_search_index AS s8 
            WHERE s8.post_id = p.ID AND s8.hash = 263456517 ) 
        AND EXISTS ( SELECT s9.post_id FROM wp_rama_search_index AS s9 
            WHERE s9.post_id = p.ID AND s9.hash = -1214274178 ) ) 
) OR ( 
    ( EXISTS ( SELECT s10.post_id FROM wp_rama_search_index AS s10 
            WHERE s10.post_id = p.ID AND s10.hash = -2064050708 ) 
        AND EXISTS ( SELECT s11.post_id FROM wp_rama_search_index AS s11 
            WHERE s11.post_id = p.ID AND s11.hash = -1864773421 ) ) 
) OR ( 
    ( EXISTS ( SELECT s12.post_id FROM wp_rama_search_index AS s12 
            WHERE s12.post_id = p.ID AND s12.hash = -1227797236 ) ) 
) OR ( 
    ( EXISTS ( SELECT s13.post_id FROM wp_rama_search_index AS s13 
            WHERE s13.post_id = p.ID AND s13.hash = 1823159221 ) 
        AND EXISTS ( SELECT s14.post_id FROM wp_rama_search_index AS s14 
            WHERE s14.post_id = p.ID AND s14.hash = -1214274178 ) ) 
) OR ( 
    ( EXISTS ( SELECT s15.post_id FROM wp_rama_search_index AS s15 
            WHERE s15.post_id = p.ID AND s15.hash = 323592937 ) ) 
) OR ( 
    ( EXISTS ( SELECT s16.post_id FROM wp_rama_search_index AS s16 
            WHERE s16.post_id = p.ID AND s16.hash = 322413837 ) ) 
) OR ( 
    ( EXISTS ( SELECT s17.post_id FROM wp_rama_search_index AS s17 
            WHERE s17.post_id = p.ID AND s17.hash = 472301092 ) ) ) ) ) 
ORDER BY p.post_date DESC

此查询从 phpMyAdmin 连接到大型 AWS Aurora 数据库实例运行大约 1.1 秒。

有 35,000 个已发布的帖子。帖子标题中的单词和帖子内容的摘录使用 FVN1A32 变形和散列。

“AND”操作表示两个哈希必须匹配的短语。有几个哈希值,因为正在使用关键字词干,并且关键字的别名也可以是短语。

【问题讨论】:

  • 具有多个除数的关系除法以防万一有人想知道...
  • 尝试AND s0.hash IN ( ........ ) LIMIT 1` 并列出那里的所有哈希值。那么你只需要一个子查询。
  • 你有 danblack 的例子吗?
  • 这是什么wp_rama_search_index 表?你使用搜索插件吗?哪一个?

标签: mysql wordpress search optimization query-optimization


【解决方案1】:
SELECT s15.post_id FROM wp_rama_search_index AS s15 
        WHERE s15.post_id = p.ID AND s15.hash = 323592937

s15的真名是什么?这可能会给我们一些关于如何改进查询的线索。另外,hash 是关于什么的?

改为简单的SELECT 1 FROM ...;在执行EXISTS 时列出任何列没有优势(也可能是劣势)。

这个组合索引,列按任意顺序排列,应该会有所帮助:

INDEX(post_id, hash)

OR 对优化来说是致命的。但是,在您的查询中,将EXISTS(...) 子句与最有可能的子句排在第一位可能是有利的。

这是 WordPress?可能是将您正在搜索的属性收集到单个列中(在wp_rama_search_index?),将FULLTEXT 索引应用于列,然后使用MATCH(ft_column) AGAINST ("foo bar baz ..."). This may do most of the ORs without needing hashes and lots of EXISTS. (Again, I am guessing what is going on with s15andhash`。 )

您对单词的预处理对于同义词仍然有用,但对于屈折变化是不必要的(因为 FULLTEXT 主要处理)。短语可以用引号处理。这可能避免您当前使用AND

如果页面加载需要 1.5 秒SELECT,我怀疑还有其他需要优化的地方。

danblock 关于用IN 替换OR 的建议是这样的:

    EXISTS ( SELECT s15.post_id FROM wp_rama_search_index AS s15 
        WHERE s15.post_id = p.ID AND s15.hash = 323592937 
           ) 
OR 
    EXISTS ( SELECT s16.post_id FROM wp_rama_search_index AS s16 
        WHERE s16.post_id = p.ID AND s16.hash = 322413837 
           ) 

-->

OR EXISTS( SELECT 1 FROM wp_rama_search_index
        WHERE post_id = p.ID
          AND hash IN ( 323592937, 322413837 )

(不需要LIMIT 1。)我对FULLTEXT 的建议取代了这一点。

使用 FULLTEXT 可能会消除由于拥有(或不拥有)ORDER BY 而导致的速度变化。

【讨论】:

  • 感谢您提供的示例。性能问题是按顺序排列的。 “hash”表示帖子标题和帖子内容摘录中单词的 fnv321a 哈希值。查询现在在不到 0.01 秒内运行,因此不需要用“in”语句替换单个单词匹配项。有几个存在语句,因为关键字词干被用于匹配搜索词的别名。关键字和别名也可以是短语。
【解决方案2】:

我认为这对你有用。这基本上是一个关系除法的问题,有多个可能的除数。

我必须说,我对 MySQL 并不完全熟悉,所以我可能会有一些轻微的语法错误。但我相信你会明白的。

将不同的搜索条件放在一个临时表中,每个组都有一个组号。

我们选择主表中的所有行,其中我们的临时表有一个组,该组中的哈希总数与这些哈希上的匹配数相同。换句话说,组中每个请求的哈希都匹配。

CREATE TABLE #temp (grp int not null, hash int not null, primary key (grp, hash));

SELECT p.ID, p.post_title, LEFT ( p.post_content, 800 ), p.post_date 
FROM wp_posts AS p
WHERE p.post_status = 'publish' AND 
    EXISTS ( SELECT 1
        FROM #temp t
        LEFT JOIN wp_rama_search_index AS s0
            ON s0.hash = t.hash AND s0.post_id = p.ID
        GROUP BY grp
        HAVING COUNT(*) = COUNT(s0.hash)
    );

还有其他方法可以对此进行切片,例如以下,可能更有效:

SELECT p.ID, p.post_title, LEFT ( p.post_content, 800 ), p.post_date 
FROM wp_posts AS p
CROSS JOIN #temp t
LEFT JOIN wp_rama_search_index AS s0
    ON s0.hash = t.hash AND s0.post_id = p.ID
WHERE p.post_status = 'publish'
GROUP BY p.ID    -- functional dependency??
HAVING COUNT(*) = COUNT(s0.hash);

另请参阅 SimpleTalk 上的这些优秀文章:Divided We Stand: The SQL of Relational DivisionHigh Performance Relational Division in SQL Server。 MySQL的基本原理应该是一样的。

如果可以的话,我会在临时表上放置一个复合索引,但不确定是哪个顺序。

【讨论】:

  • 很难跟上。有没有一个例子将两个哈希与一个“AND”操作结合起来,并将它与一个“OR”操作结合到一个哈希上?
  • 你需要从逻辑上思考你想要做什么。您的意思是您有多个哈希列表,您需要一个帖子才能完全匹配至少一个哈希列表。所以我们需要将匹配的哈希值存储在一个临时表中,并用一个分组号来显示它们属于哪个列表。然后我们进行关系划分来确定是否有任何列表与任何帖子匹配。
  • 短语和单个单词的代码在 sql 中会有所不同,但在后处理中预匹配以进行验证并不重要。我不想匹配数据库中的 50k 个帖子,所以我检查 32 位整数并将其减少到 5k 或更少的结果。我认为结合单个单词的简单连接和上面的查询仅针对短语(匹配一列的多行)将优化查询。我很好奇是否有人知道最快的可扩展方式。
  • 您现在了解这两个查询的工作原理以及它们如何回答这个问题了吗?我引用的第二篇文章详细介绍了性能。
  • 我在遵循聚合逻辑时遇到了困难,这就是为什么我要求提供更详细的示例,在该示例中,您既要返回在单个哈希上匹配的帖子,又在具有“AND”逻辑的多个哈希上匹配的帖子。也不确定交叉连接。它们是标量的,可能很危险。不确定临时表对我有什么作用,除了在应用短语的“AND”逻辑之前将结果缩小到仅可能的匹配项,这在增加算法复杂性之前不是一个坏主意。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-01-05
  • 1970-01-01
  • 1970-01-01
  • 2014-11-25
  • 2023-03-23
  • 2012-01-04
相关资源
最近更新 更多