SQL Сервер: производительность поиска шестнадцатеричных строк в больших таблицах (с использованием LIKE, полнотекстового поиска и т. Д. c.) - PullRequest
1 голос
/ 20 апреля 2020

У меня есть таблица с 40+ миллионами строк в MS SQL Server 2019. Один из столбцов содержит чистые шестнадцатеричные строки (как двоичные, так и читаемые ASCII-содержимое). Мне нужно выполнить поиск в этой таблице строк, содержащих шестнадцатеричную строку c.

Как правило, я бы сделал это:

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

Поскольку результаты разбиты на страницы, это может занять меньше секунду, чтобы найти первые 10 результатов. Однако при увеличении смещения или поиске строк, которые появляются только 1-2 раза во всей таблице, это может занять более минуты, после чего мое приложение истечет.

План выполнения для этот запрос выглядит следующим образом: execution plan

Есть ли какие-либо простые способы повысить производительность такого поиска?

Используя этот ответ, Мне удалось сократить время запроса с 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 секунд. Это не идеально, потому что мне нужны как порядок, так и нумерация страниц. Он все еще должен сканировать всю таблицу

Я попробовал следующее:

  • Создать индекс для этого столбца. Это не имеет заметного эффекта.
  • Я встречал эту статью о медленных запросах. Первоначально я использовал параметризованные запросы в своем приложении, что было намного медленнее, чем запуск их в 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 сообщений, я не могу с уверенностью сказать, что знаю, как улучшить производительность таких запросов для моего конкретного использования c. случай, если это вообще возможно. Итак, есть ли простой способ улучшить это?

1 Ответ

0 голосов
/ 20 апреля 2020

Во-первых, Kudo для фантастического c объяснения вашей проблемы. Это поможет вам быстро получить лучшие ответы. Вы также должны включить DDL, включая индексы, когда это возможно. Это станет ясно, когда я отвечу на ваш вопрос.

Я собираюсь решить пару вопросов с вашим запросом, которые не связаны с тем, как вы сейчас разбираете текст, и поговорим о том, как справиться с проблемой строк позже вечером.

Ответ. Часть 1. Не относится к разбору строк.

Вполне возможно, что поиск по строке является основной проблемой производительности. Давайте начнем с SELECT * - вам абсолютно нужны все столбцы? В частности, вам абсолютно необходимы все столбцы, включенные в этот поиск ключей? Это самая важная вещь для сортировки. Позвольте мне объяснить.

Ваш запрос выполняет сканирование вашего некластерного индекса с именем outhex-index , а затем выполняет поиск по ключу для получения строк, не включенных в outhex-index. Поиск ключей снижает производительность, , особенно в отношении кластерного и некластеризованного индекса с 40 000 000 строк.

Если вам нужны эти столбцы, то вам следует рассмотреть добавив их в виде включенных столбцов в ваш индекс outhex-index. Я говорю, рассмотрим, потому что я не знаю, сколько столбцов, ни тип данных. Включение столбцов ускоряет запросы, устраняя дорогостоящий поиск ключей, но они замедляют изменение данных, иногда значительно в зависимости от количества / типа индексов. Если вам нужны столбцы, не включенные в outhex-index, и они являются большими столбцами (типы данных MAX / BLOB / LOB, XML, et c), тогда индекс покрытия НЕ является опцией. Если они вам не нужны, тогда вы рефакторируете свое заявление SELECT *, чтобы включить только те столбцы, которые вам нужны.

Полнотекстовое индексирование здесь не вариант, если только вы не можете найти способ потерять этот вид. Сортировка имеет N log N сложность , что означает, что сортировка становится дороже, чем больше строк вы сортируете. По возможности следует избегать сортировки 40 миллионов строк. Этого будет трудно избежать с помощью полнотекстовой индексации по причинам, которые требуют больше времени для объяснения, чем у меня есть время. Добавление / изменение 40-миллионного индекса строки может быть дорогим и занимать много времени. Если вы делаете go этот маршрут, я предлагаю взять автономную копию этой таблицы, сколько времени потребуется для сборки. Вы также можете рассмотреть возможность создания отфильтрованного индекса, если это возможно, чтобы уменьшить область поиска.

Я также заметил, что оба запроса получают планы последовательного выполнения. Я не знаю, поможет ли параллельный план в первом запросе с поиском ключа, но я знаю, что он, вероятно, поможет со вторым, так как в этом есть какая-то разновидность. Планы параллельного выполнения действительно могут ускорить сортировку. Попробуйте проверить ваш запрос с помощью OPTION (QUERYTRACEON 8649) или make_parallel () Адама Мачани c.

Сегодня вечером я обновлю этот пост с некоторыми идеями, чтобы быстрее разобрать вашу строку. Одной вещью, которую вы могли бы рассмотреть в это время, является хитрый триггерный поиск строки Trigram Wildcard , который может быть вариантом.

...