在ES中,请求一旦发起,ES服务器是按照请求参数的顺序依次执行具体的搜索过滤逻辑的。如何定制请求体中的搜索过滤条件顺序,是一个经验活。
类似query(指search中的query请求参数),也是搜索的一种方式。与常见的搜索对比,filter不会计算搜索条件相关度分数,也不会根据相关度分数进行排序,相对效率更高一些。且filter内置cache,自动缓存常用的filter数据,有效提升过滤速度。语法:
GET /test_sort/_search
{
"query": {
"bool": {
"filter": {
"range": {
"order": {
"lt" : 20
}
}
},
"must": [
{
"match": {
"content": "second"
}
}
]
}
}
}
如果搜索数据需要对相关度进行排序,如百度搜索,使用搜索查询。如果搜索数据对相关度无要求,建议使用filter来提升执行效率。通常来说,如果有多个条件的话,一般都会使用其中的部分条件做filter过滤,再执行query搜索,来提高效率。
如果是单纯的过滤数据。不需要增加额外的搜索匹配(没有任何的相关度分数计算的要求),也可以使用下述语句:
GET /test_sort/_search
{
"query": {
"constant_score": {
"filter": {
"range": {
"order": {
"lte": 20
}
}
}
}
}
}
5.1 fitler 底层执行原理(bitset机制和cache机制)
5.1.1 执行流程
首先我们假设某index中有document数据如下:
POST /_bulk
{ "create" : { "_index" : "test_filter" , "_type" : "filter_type", "_id" : "1" } }
{"field" : "2011 2012 2013"}
{ "create" : { "_index" : "test_filter" , "_type" : "filter_type", "_id" : "2" } }
{"field" : "2012 2013 2014"}
{ "create" : { "_index" : "test_filter" , "_type" : "filter_type", "_id" : "3" } }
{"field" : "2013 2014 2015"}
{ "create" : { "_index" : "test_filter" , "_type" : "filter_type", "_id" : "4" } }
{"field" : "2012 2014 2011"}
{ "create" : { "_index" : "test_filter" , "_type" : "filter_type", "_id" : "5" } }
{"field" : "2011 2012 2014"}
{ "create" : { "_index" : "test_filter" , "_type" : "filter_type", "_id" : "6" } }
{"field" : "2014 2012 2011"}
那么ES会创建的倒排索引如下:
Word Docs
"2011" 1, 4, 5, 6
"2012" 1, 2, 4, 5, 6
"2013" 1, 2, 3
"2014" 2, 3, 4, 5, 6
"2015" 3
当filter条件如下:
GET /test_filter/filter_type/_search
{
"query": {
"constant_score": {
"filter": {
"term": {
"field": "2011"
}
}
}
}
}
那么这个Filter的执行流程是:
5.1.1.1 搜索
在倒排索引中查找搜索串,获取document list,此时根据搜索结果得出,符合条件的document list是id为1,4,5,6的document。
5.1.1.2 构建bitset
ES会为每个filter在倒排索引中搜索得到的结果构建一个bitset。bitset很简单,就是一个二进制的数组,数组下标对应的是当前index中document的搜索顺序(不是任何的排序,就是ES存储的顺序,也是查询的基础顺序,可以通过一个全搜索获取index的存储顺序。在现在的案例中,存储顺序为id:5、2、4、6、1、3),如我们的案例中,document总计有6个,假设ES的存储顺序就是5、2、4、6、1、3,那么bitset的下标0~5分别对应id为1~6的document。构建的bitset长度就是6。在bitset中,0代表不符合搜索条件,1代表符合搜索条件,那么我们的搜索结果对应的bitset就是:[1, 0, 1, 1, 1, 0]。ES使用bitset来标记document是否符合搜索条件,也是为了用最简单的数据结构来描述搜索结果,来节省内存的开销。
5.1.1.3 遍历bitset
因为一个搜索中可以有多个filter条件,搜索在ES执行后,filter对应的bitset可能有多个,ES会遍历每个filter对应的bitset,且从最稀疏的开始遍历,以达到效率最佳。
最稀疏代表bitset中的1最少,1最少代表这个bitset中符合搜索条件的数据最少,从这个bitset开始遍历,可以尽可能的一次性过滤掉最多的不符合条件的document。
当所有的bitset遍历结束后,所有的filter条件过滤完成。得到过滤结果。
Filter 2011 [1,0,1,1,1,0] -> 有4个1,代表有4条数据符合过滤条件
Filter 2012 [1,0,0,0,1,1] -> 有3个1,代表有3条数据符合过滤条件
先从filter2012的bitset开始迭代。因为可以一次性过滤掉3个不符合过滤条件的数据。
5.1.1.4 caching bitset
ES会缓存bitset数据到内存中,如果后续的搜索语法中,有相同的filter条件,那么从缓存中直接使用对应的bitset来过滤数据,效率最优。
ES也不是所有的bitset都缓存,其缓存机制是:最近的256个filter中,如果某个filter执行次数达到一定的数量,则缓存bitset。(一定的数量不是固定的,是有一定逻辑算法的,如:10秒内搜索10次,20秒内搜索30次, 50秒内搜索100次)
ES也有不缓存的bitset:如果数据少于1000则不缓存(index中的ducument少于1000)。如果索引分段数据不足索引整体数据的3%也不缓存。(索引分段在后续课程中详细讲解,现在简单理解为索引的一部分,简单理解为一个shard)
因为这个bitset的缓存,所以filter的执行效率是比query高的。
这个时候,搜索结果已经返回。ES在缓存bitset的同时,一般已经返回了搜索结果了。也就是一个并发的操作,搜索完成返回结果和缓存bitset同时执行。
5.1.2 执行特性
当多次filter搜索的时候,中间掺杂了数据的写入操作,那么缓存在内存中的bitset是否就是错误的?ES在filter执行过程中,也有其特性,用于解决上述问题。
5.1.2.1 query和filter的执行顺序
一般来说,ES会先执行filter,再执行query,因为filter效率高,能够过滤掉不符合搜索条件的数据。且query是需要计算相关度分数来排序的,执行效率低,如果先执行query,会导致不符合filter过滤条件的数据也参与了相关度分数计算和排序,效率降低。
5.1.2.2 bitset cache auto_update
如果document的数据修改,或新增、删除了document,那么缓存的bitset会自动更新,保证缓存bitset的数据有效性。这个自动更新是ES自动操作的,不需要关心。
ES实现bitset cache auto_update就是为了解决缓存数据与存储数据不一致的问题。
5.1.2.3 bitset cache的应用时机
只要在ES中执行query的时候包含filter过滤条件,ES都会先检查cache中是否有这个filter的bitset cache,如果有,则直接使用,如果没有,则搜索过滤index中的document。
原文地址:https://www.cnblogs.com/yucongblog/p/11985645.html