Параллельность в Lucene.NET. - PullRequest
27 голосов
/ 15 февраля 2010

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

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

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

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

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

Ответы [ 3 ]

31 голосов
/ 21 февраля 2010

Прежде всего мы должны определить операцию записи. Операция записи будет блокировать блокировку после запуска операции записи и будет продолжаться до тех пор, пока вы не закроете объект, выполняющий эту работу. Например, создание 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» или проверка правописания.

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

6 голосов
/ 15 февраля 2010

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

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

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

  • Index.Current> Index.Old
  • Index.New> Index.Current
5 голосов
/ 26 февраля 2010

Если у вас будет несколько пишущих в разных процессах, и они будут тратить больше 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++;
  }
}
...