Подтвердить что ты не робот

Как оптимизировать "текстовый поиск" для инвертированного индекса и реляционной базы данных?

Обновление 2015-10-15

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

Вопрос: Так ли вообще невозможно улучшить эту архитектуру, за исключением использования доступных сервисов, таких как elasticsearch, lucene и других?


Оригинальный вопрос

Я разрабатываю веб-приложение, в котором пользователи ищут определенные заголовки (скажем, например, book x, book y и т.д.), данные которых находятся в реляционной базе данных (MySQL).

Я следую принципу, что каждая запись, извлеченная из db, кэшируется в памяти, так что приложение имеет меньше вызовов в базе данных.

Я разработал свою собственную мини-поисковую систему со следующей архитектурой:
Architecture diagram
Вот как это работает:

  • a) Пользователь выполняет поиск имени записи
  • b) Система проверяет, с какого символа начинается запрос, проверяет, есть ли там запрос: получить запись. Если нет, добавляет его и получает все соответствующие записи из базы данных двумя способами:
    • Любой запрос уже существует в таблице "Запросы" (которая является своего рода таблицей истории), таким образом, получается запись на основе идентификаторов (Быстрая производительность)
    • Или, в противном случае, используя оператор Mysql LIKE %%, чтобы получить записи/идентификаторы (Также сохраните использованный запрос пользователем в таблице истории Запросы вместе с идентификаторами, которые он сопоставляет).
      - > Затем он добавляет записи и их идентификаторы в кеш и только идентификаторы к инвертированной карте индексов.
  • c) результаты возвращаются в пользовательский интерфейс

Система работает нормально, однако у меня есть основные проблемы Two, что я не смог найти подходящее решение для (пытался за последний месяц):

Первая проблема:
если вы проверяете точку (b), где не найден запрос "история", и он должен использовать оператор Like %%: этот процесс становится time потребляющим, когда запрос соответствует многочисленным записям в базе данных (вместо одного или двух):

  • Для получения записей из Mysql потребуется некоторое время (именно поэтому я использовал INDEXES для конкретных столбцов)
  • Тогда время для сохранения истории запросов
  • Затем время для добавления записей/идентификаторов в кеш и инвертированные карты индексов

Вторая проблема:
Приложение позволяет пользователям добавлять новые записи, которые могут сразу использоваться другими пользователями, зарегистрированными в приложении.
Однако для этого необходимо обновить карту инвертированных индексов и "запросы" таблицы, чтобы в случае, если какой-либо старый запрос соответствует новому слову. Например, если добавлена ​​ новая запись "woodX", все же старый запрос "дерево" отображает его. Поэтому, чтобы перехватить запрос "wood" на эту новую запись, вот что я делаю сейчас:

  • новая запись "woodX" добавляется в таблицу "records".
  • тогда я запустил оператор Like %%, чтобы увидеть, какой уже существующий запрос в таблице "запросы" отображает эту запись (например, "дерево" ), затем добавьте этот запрос с новым идентификатором записи как новую строку: [дерево, новый идентификатор].
  • Затем в памяти обновите инвертированный указатель. Введите значение ключа "дерево" (т.е. список), добавив новый идентификатор записи в этот список.

- > Таким образом, теперь, если удаленный пользователь ищет "дерево", он получит из памяти: дерево и дерево X

Проблема здесь также потребление времени. Согласование всех историй запросов (в табличных запросах) с добавленным словом занимает много времени (чем больше совпадающих запросов, тем больше времени). Тогда обновление памяти также занимает много времени.

То, что я считаю мышлением для исправления этой проблемы времени, заключается в том, чтобы вернуть желаемые результаты пользователю first, а затем пусть приложение POST a ajax вызовите необходимые данные для выполнения всех этих задач UPDATE. Но я не уверен, что это плохая практика или непрофессиональный способ делать что-то?
Так что за последний месяц (немного больше) я попытался подумать о лучшей оптимизации/модификации/обновлении для этой архитектуры, но я не эксперт в области поиска документов (на самом деле это моя первая мини-поисковая система, когда-либо построенная).

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

PS:

  • Это приложение j2ee, использующее сервлеты.
  • Я использую MySQL innodb (поэтому я не могу использовать полнотекстовый поиск)
4b9b3361

Ответ 1

Я бы настоятельно рекомендовал Sphinx Search Server, который лучше всего оптимизирован для полнотекстового поиска. Посетите http://sphinxsearch.com/.

Он предназначен для работы с MySQL, поэтому это дополнение к вашей текущей рабочей области.

Ответ 2

Я не претендую на то, чтобы иметь решение, но вот мои идеи. Во-первых, я, как и вы, за долгие запросы LIKE %%: я бы выполнил запрос, ограниченный несколькими ответами в MySQL, например, дюжину, вернуть его пользователю и подождать, чтобы узнать, хочет ли пользователь больше совпадающих записей или запускает в фоновом режиме полный запрос, в зависимости от ваших потребностей в индексации для будущих поисков.

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

Вот почему я думаю, что решение, которое я видел в "программном обеспечении с открытым исходным кодом" (я не помню его имени), не так уж плохо для текстового поиска в сообщениях: каждый раз, когда данные вставляются, таблица под названием "Слова" хранятся следы каждого существующего слова и другая таблица (пусть говорят "WordsLinks" ) связь между каждым словом и сообщениями, которые появляются. Такое решение имеет некоторые недостатки:

  • Каждая вставка, удаление, обновление в базе данных намного медленнее
  • Должен предвидеть выбор данных для поисковой системы: если вы решили сохранить два слова, которые вы никогда не сохраняли, слишком поздно для уже записанных данных, если только вы не начнете полную обработку данных.
  • Вы должны позаботиться о DELETE, а также UPDATE и INSERT

Но я думаю, что есть некоторые большие преимущества:

  • Время вычисления, вероятно, совпадает с "решением памяти" (в конечном итоге), но оно разделяется в каждой базе данных "Создать/обновить/удалить", а не во время запроса.
  • Поиск целого слова или слов "начиная с" происходит мгновенно: при индексировании поиск в таблице "Слова" является дихотомическим. И запрос таблицы WordLinks очень быстрый либо с индексом.
  • Поиск нескольких слов одновременно может быть простым: собрать группу "WordLinks" для каждого найденного Word и выполнить пересечение на них, чтобы сохранить только "Идентификаторы базы данных", общие для всех этих групп. Например, со словами "дерево" и "лист", первый из них может содержать записи таблицы {1, 4, 6}, а второй - {1, 3, 6, 9}. Таким образом, с пересечением просто хранить только общие части: {1, 6}.
  • "Подобно %%" в таблице с одним столбцом, вероятно, быстрее, чем много "Like %%" в разных полях разных таблиц. И каждый движок базы данных обрабатывает некоторый кеш: таблица "Words" может быть немного достаточной для хранения в памяти.
  • Я думаю, что существует небольшой риск проблем с производительностью и памятью, если данные становятся огромными.
  • Поскольку каждый поиск выполняется быстро, вы можете даже искать синонимы. Например, найдите "сеть", если пользователь ничего не нашел с "ethernet".
  • Вы можете применять правила, например, расщеплять слова верблюда для генерации, например, трех слов "дерево", "X", "woodX" из "woodX". Каждое "слово" очень легкое для хранения и поиска, поэтому вы можете многое сделать.

Я думаю, что решение, в котором вы нуждаетесь, может быть сочетанием методов: например, вы можете сохранять облегченные UPDATE, INSERT, DELETE и запускать "Words" и "WordsLinks", подавая TRIGGER.

Просто для анекдота я увидел программное обеспечение, разработанное моей компанией, в котором было принято решение сохранить "все" (!) в памяти. Это позволяет нам рекомендовать нашим клиентам покупать серверы с 64 ГБ оперативной памяти. Немного дороже. Это объясняет, почему я очень осмотрителен, когда вижу решения, которые могут в конечном итоге привести к заполнению памяти.

Ответ 3

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

Вот возможное решение:

  • Перепроектируйте свою базу данных, чтобы содержать только авторитетные данные, но не производные данные. Таким образом, все записи кэша должны исчезнуть из MySQL.

  • Храните данные только в течение продолжительности запроса в памяти в приложении. Это значительно упрощает дизайн вашего приложения (думаю, условия гонки) и позволяет масштабировать до разумного числа клиентов.

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

Вы можете взглянуть на Redis или Memcached для слоя кеширования. Я думаю, что стратегия LRU должна соответствовать здесь. В зависимости от того, насколько сложны ваши запросы, может иметь смысл и выделенный механизм индексированного поиска, такой как Lucene.

Ответ 4

Я уверен, что это может быть реализовано в MySQL, но было бы гораздо меньше усилий, чтобы просто использовать существующую поисковую базу данных, такую ​​как Elasticsearch. Он использует библиотеку Lucene для реализации инвертированного индекса, имеет обширную документацию, поддерживает горизонтальное масштабирование, довольно простой язык запросов и так далее. Я думаю, что было довольно много работы, чтобы зайти так далеко, и будет еще больше работать с кешами, условиями гонки, ошибками, проблемами производительности и т.д., Чтобы сделать решение "класс производства".