es的term 、terms 和 match、range 和缓存

es 的term 、terms 和 match、range 和缓存

本文是学习es的中文官网,自己的学习总结
整体数据插入及查询流程

数据存入

将新接收的数据存在buffer中,数据分词处理后,新建倒排索引 生成segment文件, (正常是在将数据写入磁盘时生成segment )但为了实时性, 把这个segment 刷到文件系统缓存中, 此时lucene可以检索这个新生成的segment, 为了保证数据的安全性(服务宕机丢失缓存数据), ES在在把数据写到内存buffer的时候同时还记录了一个translog日志。当异常发生时translog日志文件依然保持原样。ES会从commit位置开始,恢复整个translog文件中的记录保持数据一致性。当文件系统的缓存真正同步到磁盘上时,commit文件才更新。Translog才清空。ES默认每5秒或者每次请求操作之前都强制将translog的内容同步到磁盘上。提高了数据的安全性。ES提供了单独的/_refresh接口,默认将新生成的segment刷到文件系统缓存的时间间隔是1秒,也可以通过参数修改间隔

数据查询

数据分词后生成索引,当我们DSL语言最终都会转换成term 原子操作,然后根据term的结果进行评分

term(精确值查找)

term 查询, 可以用它处理数字(numbers)、布尔值(Booleans)、日期(dates)以及文本(text)
trem 搜索字符串时 要将字段设置成 not_analyzed 无需分析的。不然es会将字符串进行分词,分词结果建立索引,在用trem进行精确查找时找不到任何文档

//将字段 productID 设置成无需分析的字段。
PUT /my_store 
{
    "mappings" : {
        "products" : {
            "properties" : {
                "productID" : {
                    "type" : "string",
                    "index" : "not_analyzed"  
                }
            }
        }
    }
}
// 初始化查询
POST /my_store/products/_bulk
{ "index": { "_id": 1 }}
{ "price" : 10, "productID" : "XHDK-A-1293-#fJ3" }
{ "index": { "_id": 2 }}
{ "price" : 20, "productID" : "KDKE-B-9947-#kL5" }
{ "index": { "_id": 3 }}
{ "price" : 30, "productID" : "JODL-X-1937-#pV7" }
{ "index": { "_id": 4 }}
{ "price" : 30, "productID" : "QQPX-R-3956-#aD8" }

GET /my_store/products/_search
{
    "term" : {
        "price" : 20
    }
}

通常当查找一个精确值的时候,我们不希望对查询进行评分计算。只希望对文档进行包括或排除的计算,所以我们会使用 constant_score 查询以非评分模式来执行 term 查询并以一作为统一评分。(非评分模式查询也更容易被缓存)

//使用非评分模式查询
GET /my_store/products/_search
{
    "query" : {
        "constant_score" : { 
            "filter" : {
                "term" : { 
                    "price" : 20
                }
            }
        }
    }
}

过滤器的内部操作

在内部,Elasticsearch 会在运行非评分查询的时执行多个操作:

  1. 查找匹配文档.

    term 查询在倒排索引中查找 XHDK-A-1293-#fJ3 然后获取包含该 term 的所有文档。本例中,只有文档 1 满足我们要求。

  2. 创建 bitset.

    过滤器会创建一个 bitset (一个包含 0 和 1 的数组),它描述了哪个文档会包含该 term 。匹配文档的标志位是 1 。本例中,bitset 的值为 [1,0,0,0] 。在内部,它表示成一个 “roaring bitmap”,可以同时对稀疏或密集的集合进行高效编码。

  3. 迭代 bitset(s)

    一旦为每个查询生成了 bitsets ,Elasticsearch 就会循环迭代 bitsets 从而找到满足所有过滤条件的匹配文档的集合。执行顺序是启发式的,但一般来说先迭代稀疏的 bitset (因为它可以排除掉大量的文档)。

  4. 增量使用计数.

    Elasticsearch 能够缓存非评分查询从而获取更快的访问,但是它也会不太聪明地缓存一些使用极少的东西。非评分计算因为倒排索引已经足够快了,所以我们只想缓存那些我们 知道 在将来会被再次使用的查询,以避免资源的浪费。

    为了实现以上设想,Elasticsearch 会为每个索引跟踪保留查询使用的历史状态。如果查询在最近的 256 次查询中会被用到,那么它就会被缓存到内存中。当 bitset 被缓存后,缓存会在那些低于 10,000 个文档(或少于 3% 的总索引数)的段(segment)中被忽略。这些小的段即将会消失,所以为它们分配缓存是一种浪费。

实际情况并非如此(执行有它的复杂性,这取决于查询计划是如何重新规划的,有些启发式的算法是基于查询代价的),理论上非评分查询 先于 评分查询执行。非评分查询任务旨在降低那些将对评分查询计算带来更高成本的文档数量,从而达到快速搜索的目的。

trems(查找多个精确值)

term 查询对于查找单个值非常有用,但通常我们可能想搜索多个值。 比如我们想要查找价格字段值为 $20 或 $30 的文档则可以使用trems
一定要了解 terms包含(contains) 操作,而非 等值(equals) (判断)。

如果我们有一个 term(词项)过滤器 { "term" : { "tags" : "search" } } ,它会与以下两个文档 **同时 匹配:**尽管第二个文档包含除 search 以外的其他词,它还是被匹配并作为结果返回。

{ "tags" : ["search"] }
{ "tags" : ["search", "open_source"] } 

如果一定期望得到我们前面说的那种行为(即整个字段完全相等),最好的方式是增加并索引另一个字段, 这个字段用以存储该字段包含词项的数量。例如

{ "tags" : ["search"], "tag_count" : 1 }
{ "tags" : ["search", "open_source"], "tag_count" : 2 }

//搜索
GET /my_index/my_type/_search
{
    "query": {
        "constant_score" : {
            "filter" : {
                 "bool" : {
                    "must" : [
                        { "term" : { "tags" : "search" } }, 
                        { "term" : { "tag_count" : 1 } } 
                    ]
                }
            }
        }
    }
}

这个查询现在只会匹配具有单个标签 search 的文档,而不是任意一个包含 search 的文档。

match(匹配查询)

匹配查询 match 是个 核心 查询。无论需要查询什么字段, match 查询都应该会是首选的查询方式。它是一个高级 全文查询 ,这表示它既能处理全文字段,又能处理精确字段。match 查询主要的应用场景就是进行全文搜索

//初始化查询数据
PUT /my_index
{ "settings": { "number_of_shards": 1 }} 

POST /my_index/my_type/_bulk
{ "index": { "_id": 1 }}
{ "title": "The quick brown fox" }
{ "index": { "_id": 2 }}
{ "title": "The quick brown fox jumps over the lazy dog" }
{ "index": { "_id": 3 }}
{ "title": "The quick brown fox jumps over the quick dog" }
{ "index": { "_id": 4 }}
{ "title": "Brown fox brown dog" }

//执行查询
GET /my_index/my_type/_search
{
    "query": {
        "match": {
            "title": "QUICK!"
        }
    }
}

Elasticsearch 执行上面这个 match 查询的步骤是:

  1. 检查字段类型

    标题 title 字段是一个 string 类型( analyzed )已分析的全文字段,这意味着查询字符串本身也应该被分析(也就是说 title 字段在存入es 时已经会根据选定的分词器进行分词,并将分词结果都建立了索引)。

  2. 分析查询字符串

    将查询的字符串 QUICK! 传入标准分析器中,输出的结果是单个项 quick 。因为只有一个单词项,所以 match 查询执行的是单个底层 term 查询。

  3. 查找匹配文档

    term 查询在倒排索引中查找 quick 然后获取一组包含该项的文档,本例的结果是文档:1、2 和 3 。

  4. 为每个文档评分

    term 查询计算每个文档相关度评分 _score ,这是种将词频(term frequency,即词 quick 在相关文档的 title 字段中出现的频率)和反向文档频率(inverse document frequency,即词 quick 在所有文档的 title 字段中出现的频率),以及字段的长度(即字段越短相关度越高,quick 在文档的 title 字段中所占的字符长度比重)相结合的计算方式。

range 范围查询

GET /my_store/products/_search
{
    "query" : {
        "constant_score" : {
            "filter" : {
                "range" : {
                    "price" : {
                        "gte" : 20,
                        "lt"  : 40
                    }
                }
            }
        }
    }
}

数字和日期字段的索引方式使高效地范围计算成为可能。但字符串却并非如此,要想对其使用范围过滤,Elasticsearch 实际上是在为范围内的每个词项都执行 term 过滤器,这会比日期或数字的范围过滤慢许多。

字符串范围在过滤 低基数(low cardinality) 字段(即只有少量唯一词项)时可以正常工作,但是唯一词项越多,字符串范围的计算会越慢。

日期支持时间加减

//查找时间戳在过去一个小时内的所有文档,让过滤器作为一个时间 滑动窗口(sliding window) 来过滤文档
"range" : {
    "timestamp" : {
        "gt" : "now-1h"
    }
}

//查询 早于 2014 年 1 月 1 日加 1 月(2014 年 2 月 1 日 零时)
"range" : {
    "timestamp" : {
        "gt" : "2014-01-01 00:00:00",
        "lt" : "2014-01-01 00:00:00||+1M" 
    }
}

缓存

精确值查找时, 我们会使用过滤器(filters)。过滤器很重要,因为它们执行速度非常快,不会计算相关度(直接跳过了整个评分阶段)而且很容易被缓存

过滤器的内部操作中,我们介绍了过滤器是如何计算的。其核心实际是采用一个 bitset 记录与过滤器匹配的文档。Elasticsearch 积极地把这些 bitset 缓存起来以备随后使用。一旦缓存成功,bitset 可以复用 任何 已使用过的相同过滤器,而无需再次计算整个过滤器。

这些 bitsets 缓存是“智能”的:它们以增量方式更新。当我们索引新文档时,只需将那些新文档加入已有 bitset,而不是对整个缓存一遍又一遍的重复计算。和系统其他部分一样,过滤器是实时的,我们无需担心缓存过期问题。

独立的过滤器缓存

属于一个查询组件的 bitsets 是独立于它所属搜索请求其他部分的。这就意味着,一旦被缓存,一个查询可以被用作多个搜索请求。bitsets 并不依赖于它所存在的查询上下文。这样使得缓存可以加速查询中经常使用的部分,从而降低较少、易变的部分所带来的消耗。

同样,如果单个请求重用相同的非评分查询,它缓存的 bitset 可以被单个搜索里的所有实例所重用。

自动缓存行为

早期默认的行为是缓存一切可以缓存的对象。这样会把一些重用率很低和一些查询很简单的的过虑器都缓存了bitsets,这样就很不合算。缓存这些过滤器的意义不大。开发者难以区分有良好表现的缓存以及无用缓存。

为了解决问题,Elasticsearch 会基于使用频次自动缓存查询。如果一个非评分查询在最近的 256 次查询中被使用过(次数取决于查询类型),那么这个查询就会作为缓存的候选。但是,并不是所有的片段都能保证缓存 bitset 。只有那些文档数量超过 10,000 (或超过总文档数量的 3% )才会缓存 bitset因为小的片段可以很快的进行搜索和合并,这里缓存的意义不大

一旦缓存了,非评分计算的 bitset 会一直驻留在缓存中直到它被剔除。剔除规则是基于 LRU 的:一旦缓存满了,最近最少使用的过滤器会被剔除。

参考文章:https://www.elastic.co/guide/cn/elasticsearch/guide/current/foreword_id.html

展开阅读全文
©️2020 CSDN 皮肤主题: 深蓝海洋 设计师: CSDN官方博客 返回首页
实付0元
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值