Sort document by internal doc id in FetchPhase to better use LRU cache (#57273)

This change sorts the docIdsToLoad once instead of in each sub-phase.
This commit is contained in:
Boice Huang 2020-06-30 15:26:37 +08:00 committed by jimczi
parent ab65a57d70
commit 8c93f4e154
8 changed files with 30 additions and 32 deletions

View File

@ -61,6 +61,7 @@ import org.elasticsearch.search.lookup.SourceLookup;
import org.elasticsearch.tasks.TaskCancelledException; import org.elasticsearch.tasks.TaskCancelledException;
import java.io.IOException; import java.io.IOException;
import java.util.Arrays;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
@ -144,13 +145,20 @@ public class FetchPhase implements SearchPhase {
} }
try { try {
DocIdToIndex[] docs = new DocIdToIndex[context.docIdsToLoadSize()];
for (int index = 0; index < context.docIdsToLoadSize(); index++) {
docs[index] = new DocIdToIndex(context.docIdsToLoad()[context.docIdsToLoadFrom() + index], index);
}
Arrays.sort(docs);
SearchHit[] hits = new SearchHit[context.docIdsToLoadSize()]; SearchHit[] hits = new SearchHit[context.docIdsToLoadSize()];
SearchHit[] sortedHits = new SearchHit[context.docIdsToLoadSize()];
FetchSubPhase.HitContext hitContext = new FetchSubPhase.HitContext(); FetchSubPhase.HitContext hitContext = new FetchSubPhase.HitContext();
for (int index = 0; index < context.docIdsToLoadSize(); index++) { for (int index = 0; index < context.docIdsToLoadSize(); index++) {
if (context.isCancelled()) { if (context.isCancelled()) {
throw new TaskCancelledException("cancelled"); throw new TaskCancelledException("cancelled");
} }
int docId = context.docIdsToLoad()[context.docIdsToLoadFrom() + index]; int docId = docs[index].docId;
int readerIndex = ReaderUtil.subIndex(docId, context.searcher().getIndexReader().leaves()); int readerIndex = ReaderUtil.subIndex(docId, context.searcher().getIndexReader().leaves());
LeafReaderContext subReaderContext = context.searcher().getIndexReader().leaves().get(readerIndex); LeafReaderContext subReaderContext = context.searcher().getIndexReader().leaves().get(readerIndex);
int subDocId = docId - subReaderContext.docBase; int subDocId = docId - subReaderContext.docBase;
@ -165,7 +173,8 @@ public class FetchPhase implements SearchPhase {
storedToRequestedFields, subReaderContext); storedToRequestedFields, subReaderContext);
} }
hits[index] = searchHit; sortedHits[index] = searchHit;
hits[docs[index].index] = searchHit;
hitContext.reset(searchHit, subReaderContext, subDocId, context.searcher()); hitContext.reset(searchHit, subReaderContext, subDocId, context.searcher());
for (FetchSubPhase fetchSubPhase : fetchSubPhases) { for (FetchSubPhase fetchSubPhase : fetchSubPhases) {
fetchSubPhase.hitExecute(context, hitContext); fetchSubPhase.hitExecute(context, hitContext);
@ -176,7 +185,7 @@ public class FetchPhase implements SearchPhase {
} }
for (FetchSubPhase fetchSubPhase : fetchSubPhases) { for (FetchSubPhase fetchSubPhase : fetchSubPhases) {
fetchSubPhase.hitsExecute(context, hits); fetchSubPhase.hitsExecute(context, sortedHits);
if (context.isCancelled()) { if (context.isCancelled()) {
throw new TaskCancelledException("cancelled"); throw new TaskCancelledException("cancelled");
} }
@ -189,6 +198,21 @@ public class FetchPhase implements SearchPhase {
} }
} }
static class DocIdToIndex implements Comparable<DocIdToIndex> {
final int docId;
final int index;
DocIdToIndex(int docId, int index) {
this.docId = docId;
this.index = index;
}
@Override
public int compareTo(DocIdToIndex o) {
return Integer.compare(docId, o.docId);
}
}
private int findRootDocumentIfNested(SearchContext context, LeafReaderContext subReaderContext, int subDocId) throws IOException { private int findRootDocumentIfNested(SearchContext context, LeafReaderContext subReaderContext, int subDocId) throws IOException {
if (context.mapperService().hasNested()) { if (context.mapperService().hasNested()) {
BitSet bits = context.bitsetFilterCache() BitSet bits = context.bitsetFilterCache()

View File

@ -81,6 +81,8 @@ public interface FetchSubPhase {
*/ */
default void hitExecute(SearchContext context, HitContext hitContext) throws IOException {} default void hitExecute(SearchContext context, HitContext hitContext) throws IOException {}
/**
* Executes the hits level phase (note, hits are sorted by doc ids).
*/
default void hitsExecute(SearchContext context, SearchHit[] hits) throws IOException {} default void hitsExecute(SearchContext context, SearchHit[] hits) throws IOException {}
} }

View File

@ -40,9 +40,7 @@ import org.elasticsearch.search.internal.SearchContext;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
import java.util.Comparator;
import java.util.List; import java.util.List;
import java.util.Objects; import java.util.Objects;
@ -78,9 +76,6 @@ public final class FetchDocValuesPhase implements FetchSubPhase {
return; return;
} }
hits = hits.clone(); // don't modify the incoming hits
Arrays.sort(hits, Comparator.comparingInt(SearchHit::docId));
if (context.docValuesContext().fields().stream() if (context.docValuesContext().fields().stream()
.map(f -> f.format) .map(f -> f.format)
.filter(USE_DEFAULT_FORMAT::equals) .filter(USE_DEFAULT_FORMAT::equals)

View File

@ -30,8 +30,6 @@ import org.elasticsearch.search.fetch.FetchSubPhase;
import org.elasticsearch.search.internal.SearchContext; import org.elasticsearch.search.internal.SearchContext;
import java.io.IOException; import java.io.IOException;
import java.util.Arrays;
import java.util.Comparator;
import java.util.Iterator; import java.util.Iterator;
public class FetchScorePhase implements FetchSubPhase { public class FetchScorePhase implements FetchSubPhase {
@ -44,9 +42,6 @@ public class FetchScorePhase implements FetchSubPhase {
return; return;
} }
hits = hits.clone(); // don't modify the incoming hits
Arrays.sort(hits, Comparator.comparingInt(SearchHit::docId));
final IndexSearcher searcher = context.searcher(); final IndexSearcher searcher = context.searcher();
final Weight weight = searcher.createWeight(searcher.rewrite(context.query()), ScoreMode.COMPLETE, 1); final Weight weight = searcher.createWeight(searcher.rewrite(context.query()), ScoreMode.COMPLETE, 1);
Iterator<LeafReaderContext> leafContextIterator = searcher.getIndexReader().leaves().iterator(); Iterator<LeafReaderContext> leafContextIterator = searcher.getIndexReader().leaves().iterator();

View File

@ -28,8 +28,6 @@ import org.elasticsearch.search.fetch.FetchSubPhase;
import org.elasticsearch.search.internal.SearchContext; import org.elasticsearch.search.internal.SearchContext;
import java.io.IOException; import java.io.IOException;
import java.util.Arrays;
import java.util.Comparator;
public final class FetchVersionPhase implements FetchSubPhase { public final class FetchVersionPhase implements FetchSubPhase {
@Override @Override
@ -39,9 +37,6 @@ public final class FetchVersionPhase implements FetchSubPhase {
return; return;
} }
hits = hits.clone(); // don't modify the incoming hits
Arrays.sort(hits, Comparator.comparingInt(SearchHit::docId));
int lastReaderId = -1; int lastReaderId = -1;
NumericDocValues versions = null; NumericDocValues versions = null;
for (SearchHit hit : hits) { for (SearchHit hit : hits) {

View File

@ -35,7 +35,6 @@ import org.elasticsearch.search.internal.SearchContext.Lifetime;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@ -49,8 +48,6 @@ public final class MatchedQueriesPhase implements FetchSubPhase {
context.parsedQuery() == null) { context.parsedQuery() == null) {
return; return;
} }
hits = hits.clone(); // don't modify the incoming hits
Arrays.sort(hits, (a, b) -> Integer.compare(a.docId(), b.docId()));
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
List<String>[] matchedQueries = new List[hits.length]; List<String>[] matchedQueries = new List[hits.length];
for (int i = 0; i < matchedQueries.length; ++i) { for (int i = 0; i < matchedQueries.length; ++i) {

View File

@ -30,10 +30,8 @@ import org.elasticsearch.search.internal.SearchContext;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.Comparator;
import java.util.List; import java.util.List;
public final class ScriptFieldsPhase implements FetchSubPhase { public final class ScriptFieldsPhase implements FetchSubPhase {
@ -44,9 +42,6 @@ public final class ScriptFieldsPhase implements FetchSubPhase {
return; return;
} }
hits = hits.clone(); // don't modify the incoming hits
Arrays.sort(hits, Comparator.comparingInt(SearchHit::docId));
int lastReaderId = -1; int lastReaderId = -1;
FieldScript[] leafScripts = null; FieldScript[] leafScripts = null;
List<ScriptFieldsContext.ScriptField> scriptFields = context.scriptFields().fields(); List<ScriptFieldsContext.ScriptField> scriptFields = context.scriptFields().fields();

View File

@ -28,8 +28,6 @@ import org.elasticsearch.search.fetch.FetchSubPhase;
import org.elasticsearch.search.internal.SearchContext; import org.elasticsearch.search.internal.SearchContext;
import java.io.IOException; import java.io.IOException;
import java.util.Arrays;
import java.util.Comparator;
public final class SeqNoPrimaryTermPhase implements FetchSubPhase { public final class SeqNoPrimaryTermPhase implements FetchSubPhase {
@Override @Override
@ -38,9 +36,6 @@ public final class SeqNoPrimaryTermPhase implements FetchSubPhase {
return; return;
} }
hits = hits.clone(); // don't modify the incoming hits
Arrays.sort(hits, Comparator.comparingInt(SearchHit::docId));
int lastReaderId = -1; int lastReaderId = -1;
NumericDocValues seqNoField = null; NumericDocValues seqNoField = null;
NumericDocValues primaryTermField = null; NumericDocValues primaryTermField = null;