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
¿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.
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?
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.
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. –