ES在8.1版本引入了Doc-values-only search,这是个非常实用的功能。具体功能见文档:https://www.elastic.co/guide/en/elasticsearch/reference/8.1/doc-values.html#doc-value-only-fields
ES默认在开启索引的时候,也会加上doc_values。doc_values是针对字段的列式存储。
很多时候,这些字段主要是用来做聚合和排序的一些功能,索引功能几乎不用。但是加上索引本身会增加写入资源开销和存储容量的开销。去掉字段的索引大概能减少>20% 存储空间和提升>20% 构建索引吞吐。
但是去掉了字段索引,就完全无法对字段进行检索。
为了应对这种主要以聚合和排序为主,但是也要能支持检索功能的需求。ES增加了对doc_values类型的检索能力。
这样针对某个字段,可以配置成这样:
"session_id": {
"type": "long",
"index": false
}
这样session_id字段就不包含索引,只使用了doc_values。
但是从使用上,依然能像正常的long类型一样,可以进行term、range等查询。
Doc-values-only search是怎么实现的呢?
下面就以long类型为例:
@Override
public Query termQuery(String field, Object value, boolean isIndexed) {
if (hasDecimalPart(value)) {
return Queries.newMatchNoDocsQuery("Value [" + value + "] has a decimal part");
}
long v = parse(value, true);
if (isIndexed) {
return LongPoint.newExactQuery(field, v);
} else {
return SortedNumericDocValuesField.newSlowExactQuery(field, v);
}
}
在termQuery,如果字段没建索引,就走到了SortedNumericDocValuesField.newSlowExactQuery方法。
接下来的实现在lucene中,long类型使用的是SortedNumericDocValuesRangeQuery,针对term和range,都是使用SortedNumericDocValuesRangeQuery。
SortedNumericDocValuesRangeQuery查找数据的方式也比较简单,就是遍历所有doc,找到range范围内的数据。所以性能是比较差的。
针对这点,ES在针对indexing sort字段上做了优化,增加了IndexSortSortedNumericDocValuesRangeQuery,这个可以按二分查找进行doc过滤。
ES针对Doc-values-only search还能使用更加复杂的查询功能,比如keyword里的prefix、wildcard等。这些是通过Script功能实现的,实现方式就是走了runtime query的方式。