2011-06-04 20 views
11

Estoy enfrentando largos tiempos de búsqueda (orden de 10 segundos) mientras busco en una implementación maestra - fragmento en un entorno distribuido. Sin embargo, la misma consulta a través de Luke vuelve en milisegundos.¿Cómo mejorar el rendimiento de Lucene en un entorno distribuido?

La aplicación es un sistema distribuido. Todos los nodos comparten un montaje NFS común donde residen los índices. Para simplificar, consideremos dos nodos Node1 y Node2. Las entradas /etc/fstab son las siguientes.

nfs:/vol/indexes /opt/indexes nfs rw,suid,nodev,rsize=32768,wsize=32768,soft,intr,tcp 0 0 

hay varios alimentos (por ejemplo Feed1 y Feed2) que llegan al sistema y hay un fragmento de cada una de la alimentación por nodo y un maestro para cada alimentación. Los índices parecen

Feed1-master 
Feed1-shard-Node1.com 
Feed1-shard-Node1.com0 
Feed1-shard-Node1.com1 

El código que hace la búsqueda es

FeedIndexManager fim = getManager(feedCode); 
searcher = fim.getSearcher(); 
TopDocs docs = searcher.search(q, filter, start + max, sort); 

private FeedIndexManager getManager(String feedCode) throws IOException { 
    if (!_managers.containsKey(feedCode)) { 
    synchronized(_managers) { 
     if (!_managers.containsKey(feedCode)) { 
     File shard = getShardIndexFile(feedCode); 
     File master = getMasterIndexFile(feedCode); 
     _managers.put(feedCode, new FeedIndexManager(shard, master)); 
     } 
    } 
    } 
    return _managers.get(feedCode); 
} 

El FeedIndexManager es el siguiente.

public class FeedIndexManager implements Closeable { 

    private static final Analyzer WRITE_ANALYZER = makeWriterAnalyzer(); 
    private final Directory _master; 
    private SearcherManager _searcherManager; 
    private final IndexPair _pair; 

    private int _numFailedMerges = 0; 
    private DateTime _lastMergeTime = new DateTime(); 

    public FeedIndexManager(File shard, File master) throws IOException { 
    _master = NIOFSDirectory.open(master, new SimpleFSLockFactory(master)); 

    IndexWriter writer = null; 
    try { 
     writer = new IndexWriter(_master, 
           WRITE_ANALYZER, 
           MaxFieldLength.LIMITED); 
    } finally { 
     if (null != writer) { 
     writer.close(); 
     } 
     writer = null; 
    } 

    _searcherManager = new SearcherManager(_master); 
    _pair = new IndexPair(_master, 
          shard, 
          new IndexWriterBuilder(WRITE_ANALYZER)); 
    } 

    public IndexPair getIndexWriter() { 
    return _pair; 
    } 

    public IndexSearcher getSearcher() { 
    try { 
     return _searcherManager.get(); 
    } 
    catch (IOException ioe) { 
     throw new DatastoreRuntimeException(
     "When trying to get an IndexSearcher for " + _master, ioe); 
    } 
    } 

    public void releaseSearcher(IndexSearcher searcher) { 
    try { 
     _searcherManager.release(searcher); 
    } 
    catch (IOException ioe) { 
     throw new DatastoreRuntimeException(
     "When trying to release the IndexSearcher " + searcher 
     + " for " + _master, ioe); 
    } 
    } 

    /** 
    * Merges the changes from the shard into the master. 
    */ 
    public boolean tryFlush() throws IOException { 
    LOG.debug("Trying to flush index manager at " + _master 
       + " after " + _numFailedMerges + " failed merges."); 
    if (_pair.tryFlush()) { 
     LOG.debug("I succesfully flushed " + _master); 
     _numFailedMerges = 0; 
     _lastMergeTime = new DateTime(); 
     return true; 
    } 
    LOG.warn("I couldn't flush " + _master + " after " + _numFailedMerges 
      + " failed merges."); 
    _numFailedMerges++; 
    return false; 
    } 

    public long getMillisSinceMerge() { 
    return new DateTime().getMillis() - _lastMergeTime.getMillis(); 
    } 

    public long getNumFailedMerges() { 
    return _numFailedMerges; 
    } 

    public void close() throws IOException { 
    _pair.close(); 
    } 

    /** 
    * Return the Analyzer used for writing to indexes. 
    */ 
    private static Analyzer makeWriterAnalyzer() { 
    PerFieldAnalyzerWrapper analyzer = 
     new PerFieldAnalyzerWrapper(new LowerCaseAnalyzer()); 

    analyzer.addAnalyzer(SingleFieldTag.ID.toString(), new KeywordAnalyzer()); 
    // we want tokenizing on the CITY_STATE field 
    analyzer.addAnalyzer(AddressFieldTag.CITY_STATE.toString(), 
      new StandardAnalyzer(Version.LUCENE_CURRENT)); 
    return analyzer; 
    } 
} 

El asesino, que consume alrededor del 95-98% de la latencia es esta llamada, se tarda unos 20 segundos para una búsqueda, mientras que si se abre el índice a través de Lucas es en milisegundos.

TopDocs docs = searcher.search(q, filter, start + max, sort); 

He las siguientes preguntas

  1. ¿Es sano tener varios maestros por cada juego o debería reducirlo a sólo un maestro? La cantidad de elementos en el índice es de aproximadamente 50 millones.

  2. La latencia es baja en los feeds donde el número de entidades es inferior a un millón (respuesta secundaria). Los feeds en los que las entidades tienen más de 2 millones demoran unos 20 segundos. ¿Debo mantener solo 1 Shard por nodo contra 1 Shard por nodo por feed?

  3. La fusión de la Shard al maestro se intenta cada 15 segundos. ¿Debería modificarse este parámetro?

Actualmente estoy usando Lucene 3.1.0 y JDK 1.6. Los cuadros son dos núcleos de 64 bits con 8   GB de RAM. Actualmente, la JVM se ejecuta con 4   GB máx.

Cualquier sugerencia para mejorar el rendimiento es muy apreciada. Ya he llevado a cabo todo el ajuste de rendimiento estándar que generalmente prescribe Lucene. Muchas gracias por leer esta larga publicación.

+0

No entiendo lo que se distribuye aquí. Usted dice "Todos los nodos comparten un montaje NFS común donde residen los índices". ¿Entonces todas las partes están en el mismo sistema físico? Lo más probable es que NFS esté perjudicando el rendimiento en este caso. –

Respuesta

2

Esta no es la respuesta que estabas buscando, quizás, pero echa un vistazo a Elastic Search. Es una capa de servicio distribuido y agrupado alrededor de Lucene, que se consulta a través de HTTP o se puede ejecutar incrustado.

Y es rápido, bastante ridículo. Parece que ha ajustado a Lucene correctamente bajo las sábanas, al tiempo que sigue exponiendo las opciones de configuración completas de Lucene si necesita usarlas.

Realizar Lucene en un entorno distribuido es difícil, ya que está descubriendo, termina con problemas de bloqueo desagradables. ElasticSearch está destinado a resolver ese problema en particular, por lo que puede resolver los demás.

+1

Hola, gracias por la respuesta rápida. Infact ES y SOLR están sobre la mesa. pero lo que estoy peleando aquí es el código heredado que heredé. Ojalá pueda activar el interruptor, lo que entually haré. No estoy en el negocio de la optimización de búsqueda y lo dejo a los expertos en ese dominio. Habiendo dicho eso, ¿ha hecho alguna evaluación comparativa de ES sobre SOLR? y/o tiene recomendaciones para uno sobre el otro. – Andy

Cuestiones relacionadas