写在前面
本文一起看下es分布式查询的过程。
1:分布式搜索过程
分布式搜索分为两个阶段,query和fetch,即query-then-fetch
。
假定primary shard=3,replica shard=1,即3个主分片,1个副本分片。
1.1:query阶段
某data node收到请求后,作为coordinate node,随机地从六个主副本分片中选择3个分片,每个data node按照得分排序后查询from+size
数量的数据,coordinate node收集所有副本的数据,到这里query 阶段就结束了,如下图:
1.2:fetch阶段
coordinate node将所有的文档按照分数重新排序后取指定条数的文档,然后以multi get的方式从各个节点上获取详细的_source数据,如下图:
2:query-then-fetch可能的问题
2.1:性能问题
因为在fetch阶段每个节点都需要查询from+size
个数据,当出现深度分页问题时,会影响查询速度,还有就是,因为coordinate node需要汇总来自其他多个分片的数据,所以当分片数比较多时,coordinate node的压力会比较大。
之前一家公司就出现过因为设置过多的主分片数,导致查询慢的问题,后来降低分片数解决。
2.2:相关度算分
每个文档的得分都是在query过程,由各个节点根据本分片的数据来独立算分的,这样就会出现一个这样的问题,那就是,当总的数据量较少时,会导致最终结果的算分偏差比较大,当然数据量大且均匀分布时一般不会由该问题,针对少数据量多分片场景下算分不准的情况可以通过如下2种方式来解决:
1:设置主分片数为1,实际上少量的数据,也确实没必要设置>1个的分片。
2:查询时增加参数_search?search_type=dfs_query_then_fetch,此时在fetch阶段会将所有的问题重新算分,再获取从from开始的size条数据。
2.2.1:例子
首先我们来创建索引,并插入3条测试数据:
DELETE message
POST message/_doc
{
"content": "good"
}
POST message/_doc
{
"content": "good morning"
}
POST message/_doc
{
"content": "good morning everyone"
}
因为我们并没有显式指定分区数,使用的是默认的分区数1,所以此时查询,算分是正确的,如下:
POST message/_search
{
"query": {
"term": {
"content": {
"value": "good"
}
}
}
}
然后我们重新创建数据,并指定分区数为20(>1即可,但为了降低测试数据分配到同一个分区可能性,影响测试结果,所以给个稍微大一点的值)
,如下:
DELETE message
PUT message
{
"settings": {
"number_of_shards": 20
}
}
POST message/_doc?routing=1
{
"content": "good"
}
POST message/_doc?routing=2
{
"content": "good morning"
}
POST message/_doc?routing=3
{
"content": "good morning everyone"
}
先来看下数据所在的分区信息:
可以看到3条数据分别分布在了2,14,和17分区。接着我们就可以来查询,看下评分的结果是否正确了:
POST message/_search
{
"query": {
"term": {
"content": {
"value": "good"
}
}
}
}
可以看到评分都相同了,就复现了少量数据评分不准确的问题了,接着增加参数_search?search_type=dfs_query_then_fetch
看下:
评分结果就对了。但是需要注意该参数,会增加coordinate node的压力,消耗更多的CPU和处理时长,也可能有性能问题。
写在后面
参考文章列表
什么是MySQL深度分页问题如何解决? 。