SQL Server:在大表中搜索十六进制字符串的性能(使用LIKE,全文搜索等)

问题描述 投票:1回答:1

我在MS SQL Server 2019中有一个包含40+百万行的表。其中一列存储纯十六进制字符串(二进制和可读ASCII内容)。我需要在此表中搜索包含特定十六进制字符串的行。

通常,我会这样做:

SELECT * FROM transactionoutputs WHERE outhex LIKE '%74657374%' ORDER BY id DESC OFFSET 0 ROWS FETCH NEXT 10 ROWS ONLY;

由于对结果进行了分页,因此可以花不到一秒钟的时间来找到前10个结果。但是,当增加偏移量或搜索仅在整个表中出现1-2次的字符串时,可能需要一分钟以上的时间,此时我的应用程序将超时。

此查询的执行计划是这样的:execution plan

有没有简单的方法可以改善这种搜索的性能?

使用this答案,我能够将查询时间从33秒减少到27秒:

SELECT * FROM transactionoutputs WHERE
CHARINDEX('74657374' collate Latin1_General_BIN, outhex collate Latin1_General_BIN) > 0
ORDER BY id DESC OFFSET 0 ROWS FETCH NEXT 10 ROWS ONLY;

[省略ORDER BY和分页时,可以将其减少到19秒。这是不理想的,因为我需要排序和分页。它仍然必须扫描整个表

我尝试了以下操作:

  • 在该列上创建索引。这没有明显的效果。
  • 我遇到this article关于慢查询的问题。最初,我在应用程序中使用参数化查询,这比在SSMS中运行它们要慢得多。此后,我移到了上面显示的查询,但是它仍然很慢。

  • 我试图启用多个活动结果集(MARS),但查询时间没有任何改善。

我也尝试过使用全文搜索。这似乎是最有前途的解决方案,因为文本搜索正是我所需要的。我创建了全文索引,可以执行类似上面的查询,但是使用索引:

SELECT * FROM transactionoutputs WHERE CONTAINS(outhex,'7465') ORDER BY id desc OFFSET 0 ROWS FETCH NEXT 10 ROWS ONLY;

这几乎立即返回结果。但是,当查询长于几个字符(通常为4个字符)时,它不返回任何内容。我是在做错什么,还是为什么要这样做?

执行计划:

execution plan full-text search

我的理解是,我的案例不是FTS的理想用例,因为它旨在搜索可读文本而不是十六进制字符串。是否仍然可以使用,如果可以,如何使用?

阅读了数十篇文章和SO帖子后,我什至不能自信地说,即使有可能,我也知道如何针对我的特定用例提高此类查询的性能。那么,有没有简单的选择可以改善这一点?

sql sql-server database full-text-search database-performance
1个回答
0
投票

首先,工藤为您的问题提供了精彩的解释。这有助于您快速获得更好的答案。您还应该包括DDL,并在可能时包括索引。当我回答您的问题时,这将很清楚。

我将解决与您的查询有关的几个问题,这些问题与您现在如何解析文本以及今晚晚些时候讨论如何处理字符串问题无关。

答案第1部分:与字符串解析无关

很有可能,您搜索字符串的方式是主要的性能问题。让我们从SELECT *开始-您是否绝对需要所有列? 具体来说,您是否绝对需要该Key查找中包括的所有列?整理这是最重要的事情。让我解释一下。

您正在查询的是对名为outhex-index的非聚集索引执行的扫描,然后执行Key查找以检索outhex-index中未包括的行。 Key lookups破坏性能,尤其是针对具有40,000,000行的聚簇和非聚簇索引。

如果确实需要这些列,则应该考虑将它们作为包含的列添加到您的outhex-index索引中。我说考虑是因为我不知道有多少列或数据类型。包含列通过消除代价高昂的键查找来加快查询速度,但它们减慢了数据修改的速度,有时视索引的数量/类型而定。如果您需要outhex-index中不包含的列并且它们是大列(MAX / BLOB / LOB数据类型,XML等),那么覆盖索引是不可行的。如果不需要它们,则可以将SELECT *语句重构为仅包含所需的列。

这里没有全文索引的选项,除非您找到丢失这种排序的方法。排序具有N log N复杂度,这意味着您排序的行越多,排序的成本就越高。尽可能避免使用4000万行排序。全文索引很难避免这种情况,原因是需要更多时间来解释,然后我才有时间。添加/修改4000万行索引可能会很昂贵,并且会花费大量时间。如果您确实选择了该方法,建议您将该表的离线副本用于建立时间。如果可能,您还可以考虑添加创建过滤索引以缩小搜索范围。

我也注意到,两个查询都在获取串行执行计划。我不知道并行计划是否可以通过关键字查找来帮助第一个查询,但是我知道它可能会帮助第二个查询,因为涉及一种排序。并行执行计划可以真正加快排序速度。考虑使用Adam Machanic的OPTION(QUERYTRACEON 8649)或make_parallel()测试查询。

我今晚将通过一些想法来更新这篇文章,以更快地解析您的字符串。同时,您可能要考虑的一件事是保罗·怀特(Paul White)的聪明Trigram Wildcard String Search技巧,这也许是一种选择。

© www.soinside.com 2019 - 2024. All rights reserved.