Параллелизм в Lucene.NET.

Я хочу использовать Lucene.NET для полнотекстового поиска, совместно использованного двумя приложениями: каждый - приложение MVC ASP.NET, и другой является консольным приложением. Оба приложения, как предполагается, ищут и обновляют индекс. Как параллелизм должен быть обработан?
Я нашел учебное руководство на ifdefined.com, где подобный вариант использования обсужден. Мое беспокойство - то, что блокировка будет большим узким местом.

PS: Также я заметил, что IndexSearcher использует снимок индекса, и в учебном руководстве, упомянутом выше искателя, создается только, когда индекс обновляется. Действительно ли это - хороший подход? Я могу просто создать регулярный объект искателя при каждом поиске и если да, каковы издержки?

Я нашел, что связанный вопрос Делает Lucene. Сеть управляет несколькими потоками, получающими доступ к тому же индексу, одна индексация, в то время как другой ищет? что утверждает, что межпроцессный параллелизм безопасен. Это означает, что это, не никакие условия состязания для индекса?

Также один очень важный аспект. Что производительность поражена включенная, если скажем, 10-15 потоков пытаются обновить индекс Lucene через получение коллективной блокировки, представленной в этом решении?

После использования его несколько месяцев я должен добавить, что открытие индекса для поиска часто может создавать исключение OutOfMemory под высоким ЦП и загрузки в память, если запрос использует сортировку. Стоимость индекса, вводная операция является маленькой (по моему опыту), но стоимость GC может быть довольно высокой.

27
задан Community 23 May 2017 в 10:29
поделиться

3 ответа

Прежде всего, мы должны определить операцию «записи». Операция записи будет возражать против блокировки, как только вы начнете операцию записи, и будет продолжаться до тех пор, пока вы не закроете объект, который выполняет эту работу. Например, создание IndexWriter и индексация документа вызовут блокировку записи для объекта, и эта блокировка будет сохраняться до тех пор, пока вы не закроете IndexWriter.

Теперь мы можем немного поговорить о замке. Эта блокировка, являющаяся объектом, является блокировкой на основе файла. Как и в случае с мифом, упомянутым ранее, создается файл с именем «write.lock». Если против блокировки записи возражают, она становится исключительной! Эта блокировка заставляет все операции изменения индекса (IndexWriter и некоторые методы из IndexReader) ждать, пока блокировка не будет снята.

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

1) Возможное решение №1 Прямые операции

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

2) Возможное решение №2. Веб-служба

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

3) создать другой индекс, а затем выполнить слияние

Если консольное приложение выполняет тяжелую работу над индексом, вы можете проверить наличие консольного приложения, вы можете создать отдельный индекс в консольном приложении, а затем объединить индексы в безопасное запланированное время с помощью IndexWriter.AddIndexes.

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

4) Индексирование и поиск по нескольким индексам

Лично я считаю, что людям нужно разделять свои индексы. Это помогает разделить обязанности программ и минимизировать время простоя и поддерживать единую точку для всех индексов. Например, если ваше консольное приложение отвечает только за добавление в определенные поля или вы как бы расширяете индекс, вы можете искать отдельные индексы, но поддерживать идентичность, используя поле идентификатора в каждом документе. Теперь вы можете воспользоваться встроенной поддержкой поиска по нескольким индексам с помощью класса MultiSercher. Или, если вы хотите, есть еще хороший класс ParallelMultiSearch, который может выполнять поиск по обоим индексам одновременно.

5) Изучите SOLR.

Что-то еще, что может помочь в решении вашей проблемы с поддержанием единого места для вашего индекса, вы можете изменить свою программу для работы с сервером SOLR. http: //lucene.apache.org / solr / есть также хорошая библиотека SOLRNET http://code.google.com/p/solrnet/ , которая может быть полезна в этой ситуации. Хотя у меня нет опыта работы с solr, у меня сложилось впечатление, что это поможет вам справиться с подобной ситуацией. Также он имеет другие преимущества, такие как выделение нажатий и поиск связанных элементов путем поиска элементов «MoreLikeThis» или обеспечение проверки орфографии.

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

31
ответ дан 28 November 2019 в 05:22
поделиться

У меня также есть поисковый индекс Lucene, который используется несколькими клиентами. Я решил эту проблему, сделав «Службу поиска Lucene» отдельной веб-службой, работающей в собственном домене приложения. Поскольку оба клиента обращаются к одной и той же веб-службе для поиска или обновления индекса, я могу сделать его потокобезопасным с помощью блокировок индексаторов Lucene.

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

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

  • Index.Current> Index.Old
  • Index.New> Index.Current
6
ответ дан 28 November 2019 в 05:22
поделиться

Если у вас будет несколько писателей в разных процессах, и они потратят более 10 секунд на запись своих изменений в индекс (что приведет к тайм-ауту ожидающих писателей), то вы можете синхронизировать доступ между процессами, используя named Мьютексы . Просто откройте / создайте Mutex с одинаковым глобальным именем в каждом приложении и используйте Mutex.WaitOne перед записью и Mutex.ReleaseMutex после записи.

var mut = Mutex.OpenExisting("myUniqueMutexName"); // wrap in try..catch to create if non-existent
mut.WaitOne();
try {
  // write logic
}
finally {
  // recover from write failure
  mut.ReleaseMutex();
}

Вероятно, лучше сделать Mutex синглтоном, поскольку их создание немного дорого.

Обновление (за комментарием):

Если процессы находятся на разных машинах, я думаю, что ваша единственная альтернатива - это наложить собственную блокировку файловой системы (используя старые файлы блокировки) для синхронизации доступа. Поскольку встроенная блокировка в любом случае использует блокировки файловой системы, я бы рекомендовал вам просто увеличивать тайм-аут IndexWriter каждый раз, когда вы его создаете.

var iw = new IndexWriter();
iw.WRITE_LOCK_TIMEOUT = 60000;

Вы также можете просто повторить попытку определенное количество раз.

var committed = false;
var attempts = 0;
while(!committed && attempts < 10) {
  try {
    // write logic
    committed = true;
  } catch (LockObtainFailedException) {
    attempts++;
  }
}
5
ответ дан 28 November 2019 в 05:22
поделиться
Другие вопросы по тегам:

Похожие вопросы: