关键词: elastic search,
今天,做了这样一个简单的搜索,es2.4.1:
POST http://127.0.0.1:9001/mp_alias_v2/news/_search
{
"query":{
"term":{
"cityId" : 1
}
}
}
通过postman发送请求,但是会获得两种搜索答案。
产生现象的原因
es中主副分片进行refresh过程是相互独立的,当批量持续写入数据时,主副分片中的数据不是严格一致的。 所以同个搜索多次执行的时候,在主副分片来回执行会导致结果“跳动”的情况。
使用preference参数指定搜索的分片就可以保证一次搜索只有一次结果。
POST http://127.0.0.1:9001/mp_alias_v2/news/_search?preference=_primary
{
"query":{
"term":{
"cityId" : 1
}
}
}
上面我们指定了主分片进行搜索,只得到一种搜索结果。也可以换成preference=_replica,得到的就是另一种结果了。
官方解释的理解
官方文档有对主副片之间统计信息不一致的问题的解释。
Now why is it a problem? Index statistics are an important part of the score. And these index statistics may be different across copies of the same shard due to deleted documents. As you may know when documents are deleted or updated, the old document is not immediately removed from the index, it is just marked as deleted and it will only be removed from disk on the next time that the segment this old document belongs to is merged. However for practical reasons, those deleted documents are taken into account for index statistics. So imagine that the primary shard just finished a large merge that removed lots of deleted documents, then it might have index statistics that are sufficiently different from the replica (which still have plenty of deleted documents) so that scores are different too.
意思是当索引有update/delete一类操作的时候,旧文档不是马上被删除的,实际的删除操作发生在一些segment合并(merge操作)的阶段。而主副分片的segment合并完全是各不想干独立进行的,所以还未删除的旧文档是不一致的。 而出于一些实际因素的考虑,还未物理删除的文档,也是shard统计信息的一部分,这样就会导致不同分片可能存在打分的差异。
通过指令 http://127.0.0.1:9001/_cat/shards
查看该索引mp_alias_v2的每个分片上文档数和每个分片上所有segment文件所占空间。
记录数是统一的。p1分片==r1分片==1122621条。p0和r0也是如此。
但是segments所占空间是不同的,有一些被删除的segment还未被实际删除掉。
那这些多余的segement文件会影响打分吗?
会的。
我们知道filter不会影响query的算分,那我将查询内容改成如下
POST http://127.0.0.1:9001/mp_alias_v2/news/_search
{
"query":{
"term":{
"cityId" : 1
}
} ,
"filter":{
"term":{
"id" : 4445541
}
}
}
这样的查询中,只有query部分是算分的,filter不会影响算分。
会得到id=4445541这篇文章的内容,但是有两个算分1.0631204 和1.0596588,说明多余的segment会影响最终的算分。
当没有指定排序字段时,默认按照打分从高到低排序,得分1.0596588必定被排在了后面。
当算分一致的情况呢?
对于score一样的匹配文档,ES是按照doc ID序返回。 同一个文档写入主副分片的doc ID并不是完全一致的,当要求返回TOP N的文档时,就可能存在主副分片给的结果不一致。
要注意区分es字段_id和segment file中的 docID。
- _id是ES设计的一个特殊字段,用来标识一个文档在索引里全局唯一的一个id。
- docID是lucene层面的一个根据文档写入顺序递增的一个顺序id,倒排索引的post list里存的就是这个id,是lucene内部用来寻址文档位置的一个id。docId是lucene内部设计,ES不对外暴露。
比如用filter代替query进程搜索时,filter是不算分的。默认就会按照docId来排序。
可以试试这个查询。
POST http://127.0.0.1:9001/mp_alias_v2/news/_search
{
"filter":{
"term":{
"cityId" : 1
}
}
}
filter是不算分的,所有的返回结果不会按照算分排序,会按照es中docId的顺序返回结果。主副分片中docId顺序不一致会导致同一次搜索有两个结果。
如何保证主副分片数据一致
将副本分片调整成0,然后再调整回原来的参数。
通过指令 http://127.0.0.1:9001/_cat/shards 查看
对应的主副分片大小是一致的,一次检索只会返回一个结果。