Elasticsearch笔记2

路由一个文档到一个分片中

当索引一个文档的时候,文档会被存储到一个主分片中。
Elasticsearch通过如下公式计算一个文档应该存放到哪个分片中:

shard = hash(routing) % number_of_primary_shards
  • routing 是一个可变值,默认是文档的 _id ,也可以设置成一个自定义的值。routing 通过 hash 函数生成一个数字,然后这个数字再除以 number_of_primary_shards (主分片的数量)后得到 余数 。这个分布在 0 到 number_of_primary_shards-1 之间的余数,就是我们所寻求的文档所在分片的位置。
  • 所有的文档 API( get 、 index 、 delete 、 bulk 、 update 以及 mget )都接受一个叫做 routing 的路由参数 ,通过这个参数我们可以自定义文档到分片的映射。一个自定义的路由参数可以用来确保所有相关的文档——例如所有属于同一个用户的文档——都被存储到同一个分片中。

主分片和副本分片如何交互

假设有一个集群由三个节点组成。 它包含一个叫blogs的索引,有两个主分片,每个主分片有两个副本分片。

在这里插入图片描述
我们可以发送请求到集群中的任一节点。每个节点都有能力处理任意请求。每个节点都知道集群中任一文档位置,所以可以直接将请求转发到需要的节点上。在下面的例子中,将所有的请求发送到 Node 1 ,我们将其称为协调节点(当发送请求的时候, 为了扩展负载,更好的做法是轮询集群中所有的节点)。

新建、索引、删除文档

在这里插入图片描述
以下是在主副分片和任何副本分片上面 成功新建,索引和删除文档所需要的步骤顺序:

  • 客户端向 Node 1 发送新建、索引或者删除请求。
  • 节点使用文档的 _id 确定文档属于分片 0 。请求会被转发到 Node 3,因为分片 0 的主分片目前被分配在 Node 3 上。
  • Node 3 在主分片上面执行请求。如果成功了,它将请求并行转发到 Node 1 和 Node 2 的副本分片上。一旦所有的副本分片都报告成功, Node 3 将向协调节点报告成功,协调节点向客户端报告成功。
  • 在客户端收到成功响应时,文档变更已经在主分片和所有副本分片执行完成,变更是安全的。

有一些可选的请求参数允许您影响这个过程:

  • consistency:在默认设置下,即使仅仅是在试图执行一个_写_操作之前,主分片都会要求必须要有规定数量(quorum)(或者换种说法,也即必须要有大多数)的分片副本处于活跃可用状态,才会去执行_写_操作(其中分片副本可以是主分片或者副本分片)。这是为了避免在发生网络分区故障的时候进行_写_操作,进而导致数据不一致。规定数量即:int( (primary + number_of_replicas) / 2 ) + 1
  • consistency 参数的值可以设为 one (只要主分片状态 ok 就允许执行_写_操作),all(必须要主分片和所有副本分片的状态没问题才允许执行_写_操作), 或 quorum 。默认值为 quorum , 即大多数的分片副本状态没问题就允许执行_写_操作。
  • timeout:如果没有足够的副本分片会发生什么? Elasticsearch会等待,希望更多的分片出现。默认它最多等待1分钟。 如果需要,可使用 timeout 参数使它更早终止:100是100毫秒,30s 是30秒。

取回一个文档

1、客户端构建请求至任意node,该节点成为协调节点。
2、协调节点根据文档id进行hash来计算属于哪个分片。
3、使用随机轮询算法以负载均衡,在主分片以及其所有副本分片中随机选择一个。
4、接收请求的分片返回文档给协调节点。
5、协调节点返回文档给客户端。

局部更新文档

在这里插入图片描述
1、客户端向 Node 1 发送更新请求。
2、它将请求转发到主分片所在的 Node 3 。
3、Node 3 从主分片检索文档,修改 _source 字段中的 JSON ,并且尝试重新索引主分片的文档。 如果文档已经被另一个进程修改,它会重试步骤 3 ,超过 retry_on_conflict 次后放弃。
4、如果 Node 3 成功地更新文档,它将新版本的文档并行转发到 Node 1 和 Node 2 上的副本分片,重新建立索引。一旦所有副本分片都返回成功, Node 3 向协调节点也返回成功,协调节点向客户端返回成功。
update API还接受routing、replication、consistency和timeout参数。
重点:当主分片把更改转发到副本分片时, 它不会转发更新请求。 相反,它转发完整文档的新版本。

多文档模式

mget将整个多文档请求分解成每个分片的多文档请求,并且将这些请求并行转发到每个参与节点。协调节点一旦收到来自每个节点的应答,就将每个节点的响应收集整理成单个响应,返回给客户端。
在这里插入图片描述
以下是使用单个 mget 请求取回多个文档所需的步骤顺序:
1、客户端向 Node 1 发送 mget 请求。
2、Node 1 为每个分片构建多文档获取请求,然后并行转发这些请求到托管在每个所需的主分片或者副本分片的节点上。一旦收到所有答复, Node 1 构建响应并将其返回给客户端。
可以对 docs 数组中每个文档设置 routing 参数。
bulk API允许在单个批量请求中执行多个创建、索引、删除和更新请求。
在这里插入图片描述
1、客户端向 Node 1 发送 bulk 请求。
2、Node 1 为每个节点创建一个批量请求,并将这些请求并行转发到每个包含主分片的节点主机。
3、主分片一个接一个按顺序执行每个操作。当每个操作成功时,主分片并行转发新文档(或删除)到副本分片,然后执行下一个操作。 一旦所有的副本分片报告所有操作成功,该节点将向协调节点报告成功,协调节点将这些响应收集整理并返回给客户端。
bulk API 还可以在整个批量请求的最顶层使用 consistency 参数,以及在每个请求中的元数据中使用 routing 参数。
空搜索
GET /_search 它简单地返回集群中所有索引下的所有文档
/_search
在所有的索引中搜索所有的类型
/gb/_search
在 gb 索引中搜索所有的类型
/gb,us/_search
在 gb 和 us 索引中搜索所有的文档
/g*,u*/_search
在任何以 g 或者 u 开头的索引中搜索所有的类型
/gb/user/_search
在 gb 索引中搜索 user 类型
/gb,us/user,tweet/_search
在 gb 和 us 索引中搜索 user 和 tweet 类型
/_all/user,tweet/_search
在所有的索引中搜索 user 和 tweet 类型
分页
size
显示应该返回的结果数量,默认是 10
from
显示应该跳过的初始结果数量,默认是 0
GET /_search?size=5&from=10
在分布式系统中深度分页
理解为什么深度分页是有问题的,我们可以假设在一个有5个主分片的索引中搜索。当我们请求结果的第一页(结果从1到10),每一个分片产生前10的结果,并且返回给协调节点 ,协调节点对50个结果排序得到全部结果的前10个。 现在假设我们请求第 1000 页—​结果从 10001到10010 。所有都以相同的方式工作除了每个分片不得不产生前10010个结果以外。 然后协调节点对全部50050个结果排序最后丢弃掉这些结果中的 50040个结果。

搜索

轻量搜索

GET /_all/tweet/_search?q=tweet:elasticsearch

_all字段

  • 这个简单搜索返回包含 mary 的所有文档:GET /_search?q=mary
  • 当索引一个文档的时候,Elasticsearch 取出所有字段的值拼接成一个大的字符串,作为 _all 字段进行索引。例如,当索引这个文档时:
    {
    “tweet”: “However did I manage before Elasticsearch?”,
    “date”: “2014-09-14”,
    “name”: “Mary Jones”,
    “user_id”: 1
    }
    这就好似增加了一个名叫 _all 的额外字段:
    “However did I manage before Elasticsearch? 2014-09-14 Mary Jones 1”

除非设置特定字段,否则查询字符串就使用 _all 字段进行搜索。
我们经常在生产环境中更多地使用功能全面的 request body 查询API,除了能完成以上所有功能,还有一些附加功能。

映射和分析

Elasticsearch 中的数据可以概括的分为两类:精确值和全文。
全文是指文本数据,且通常是指非结构化的数据。为了促进全文域的查询,Elasticsearch 首先分析文档,之后根据结果创建倒排索引 。

倒排索引
假设我们有两个文档,每个文档的content域包含如下内容:
1、The quick brown fox jumped over the lazy dog
2、Quick brown foxes leap over lazy dogs in summer
为了创建倒排索引,我们首先将每个文档的 content 域拆分成单独的 词(我们称它为 词条 或 tokens ),创建一个包含所有不重复词条的排序列表,然后列出每个词条出现在哪个文档。
在这里插入图片描述
现在,如果我们想搜索 quick brown ,我们只需要查找包含每个词条的文档:
在这里插入图片描述
你只能搜索在索引中出现的词条,所以索引文本和查询字符串必须标准化为相同的格式。分词和标准化的过程称为分析。
分析
首先,将一块文本分成适合于倒排索引的独立的词条 ,
之后,将这些词条统一化为标准格式以提高它们的“可搜索性”,或者 recall。
分析器
分析器用来完成上面分析的工作,它主要封装了三个功能:

  • 字符过滤器:字符串按顺序通过每个字符过滤器 。他们的任务是在分词前整理字符串。一个字符过滤器可以用来去掉HTML,或者将 & 转化成 and。
  • 分词器:其次,字符串被分词器分为单个的词条。一个简单的分词器遇到空格和标点的时候,可能会将文本拆分成词条。
  • Token 过滤器:最后,词条按顺序通过每个 token 过滤器 。这个过程可能会改变词条(例如,小写化 Quick成为quick ),删除词条(例如, 像 a, and, the 等无用词),或者增加词条(例如,像 jump 和 leap 这种同义词)。

内置的分析器
标准分析器(默认)、简单分析器、空格分析器、语言分析器
什么时候使用分析器
当我们 索引 一个文档,它的全文域被分析成词条以用来创建倒排索引。 但是,当我们在全文域搜索的时候,我们需要将查询字符串通过相同的分析过程 ,以保证我们搜索的词条格式与索引中的词条格式一致。
1、当你查询一个全文域时,会对查询字符串应用相同的分析器,以产生正确的搜索词条列表。
2、当你查询一个精确值域时,不会分析查询字符串,而是搜索你指定的精确值。
映射
取得索引 gb 中类型 tweet 的映射:

GET /gb/_mapping/tweet
  • 核心简单域类型:
    1、字符串: string
    2、整数 : byte, short, integer, long
    3、浮点数: float, double
    4、布尔型: boolean
    5、日期: date
  • 复杂核心域类型:null值,数组,和对象

复杂核心域类型
1、多值域

{ "tag": [ "search", "nosql" ]}

对于数组,没有特殊的映射需求。任何域都可以包含0、1或者多个值,就像全文域分析得到多个词条。
数组是以多值域 索引的—可以搜索,但是无序的。在搜索的时候,你不能指定 “第一个” 或者 “最后一个”。
2、空域
在 Lucene 中是不能存储 null 值的,所以我们认为存在 null 值的域为空域。下面三种域被认为是空的,它们将不会被索引:

"null_value":               null,
"empty_array":              [],
"array_with_null_value":    [ null ]

3、对象

{
    "tweet":            "Elasticsearch is very flexible",
    "user": {
        "id":           "@johnsmith",
        "gender":       "male",
        "age":          26,
        "name": {
            "full":     "John Smith",
            "first":    "John",
            "last":     "Smith"
        }
    }
}

Lucene 不理解内部对象。 Lucene 文档是由一组键值对列表组成的。为了能让 Elasticsearch 有效地索引内部类,它把我们的文档转化成这样:

{
    "tweet":            [elasticsearch, flexible, very],
    "user.id":          [@johnsmith],
    "user.gender":      [male],
    "user.age":         [26],
    "user.name.full":   [john, smith],
    "user.name.first":  [john],
    "user.name.last":   [smith]
}

简单扁平的文档中,没有 user 和 user.name 域。Lucene 索引只有标量和简单值,没有复杂数据结构。
最后,考虑包含内部对象的数组是如何被索引的。 假设我们有个 followers 数组:

{
    "followers": [
        { "age": 35, "name": "Mary White"},
        { "age": 26, "name": "Alex Jones"},
        { "age": 19, "name": "Lisa Smith"}
    ]
}

这个文档会像我们之前描述的那样被扁平化处理:

{
    "followers.age":    [19, 26, 35],
    "followers.name":   [alex, jones, lisa, smith, mary, white]
}

{age: 35} 和 {name: Mary White} 之间的相关性已经丢失了,因为每个多值域只是一包无序的值,解决这个问题可以通过 nested 对象。
自定义域映射
string域映射的两个最重要属性是index和analyzer,index 属性默认是 analyzed 。其他简单类型(例如 long ,double ,date等)也接受 index 参数,但有意义的值只有no和not_analyzed ,因为它们永远不会被分析。
1、index
index 属性控制怎样索引字符串。它可以是下面三个值:

  • analyzed:首先分析字符串,然后索引它。换句话说,以全文索引这个域。
  • not_analyzed:索引这个域,所以它能够被搜索,但索引的是精确值。不会对它进行分析。
  • no:不索引这个域。这个域不会被搜索到。

2、 analyzer:对于 analyzed 字符串域,用 analyzer 属性指定在搜索和索引时使用的分析器。默认, Elasticsearch 使用 standard 分析器, 但你可以指定一个内置的分析器替代它,例如 whitespace 、 simple 和 english。

请求体查询

一个带请求体的查询允许我们使用查询领域特定语言(query domain-specific language) 或者Query DSL来写查询语句。
查询表达式:
要使用这种查询表达式,只需将查询语句传递给 query 参数:
{
“query”: {
“match”: {
“tweet”: “elasticsearch”
}
}
}
合并查询语句
查询语句(Query clauses) 就像一些简单的组合块,这些组合块可以彼此之间合并组成更复杂的查询。
这些语句可以是如下形式:

  • 叶子语句(Leaf clauses) (就像 match 语句) 被用于将查询字符串和一个字段(或者多个字段)对比。
  • 复合(Compound) 语句 主要用于合并其它查询语句。
    比如,一个 bool 语句允许在你需要的时候组合其它语句,无论是 must 匹配、 must_not 匹配还是 should 匹配,同时它可以包含不评分的过滤器(filters)

查询与过滤
Elasticsearch 使用的查询语言(DSL)拥有一套查询组件,这些组件可以以无限组合的方式进行搭配。这套组件可以在以下两种情况下使用:过滤情况(不影响评分)和查询情况。
一个评分查询计算每一个文档与此查询的相关程度,同时将这个相关程度分配给表示相关性的字段 _score,并且按照相关性对匹配到的文档进行排序。
性能差异:

  • 过滤查询(Filtering queries)只是简单的检查包含或者排除,这就使得计算起来非常快。考虑到至少有一个过滤查询(filtering query)的结果是 “稀少的”(很少匹配的文档),并且经常使用不评分查询(non-scoring queries),结果会被缓存到内存中以便快速读取,所以有各种各样的手段来优化查询结果。
  • 相反,评分查询(scoring queries)不仅仅要找出匹配的文档,还要计算每个匹配文档的相关性,计算相关性使得它们比不评分查询费力的多。同时,查询结果并不缓存。
  • 过滤(filtering)的目标是减少那些需要通过评分查询(scoring queries)进行检查的文档。

常用查询

  • match_all 查询简单的匹配所有文档{ “match_all”: {}}
  • match无论任何字段上进行的是全文搜索还是精确查询,他都可以。如果你在一个全文字段上使用 match 查询,在执行查询前,它将用正确的分析器去分析查询字符串,如果在一个精确值的字段上使用它,例如数字、日期、布尔或者一个 not_analyzed 字符串字段,那么它将会精确匹配给定的值。
  • multi_match 查询可以在多个字段上执行相同的 match 查询。
    {
    “multi_match”: {
    “query”: “full text search”,
    “fields”: [ “title”, “body” ]
    }
    }
  • range 查询找出那些落在指定区间内的数字或者时间:gt大于 gte大于等于 lt小于 lte小于等于
  • term 查询被用于精确值匹配,这些精确值可能是数字、时间、布尔或者那些 not_analyzed 的字符串:term 查询对于输入的文本不分析 ,所以它将给定的值进行精确查询。
  • terms 查询和 term 查询一样,但它允许你指定多值进行匹配。如果这个字段包含了指定值中的任何一个值,那么这个文档满足条件:
    { “terms”: { “tag”: [ “search”, “full_text”, “nosql” ] }}
  • exists 查询和 missing 查询被用于查找那些指定字段中有值 (exists) 或无值 (missing) 的文档。这与SQL中的 IS_NULL (missing) 和 NOT IS_NULL (exists) 在本质上具有共性

组合多查询
用 bool 查询来实现。这种查询将多查询组合在一起。

  • must:文档必须匹配这些条件才能被包含进来。
  • must_not:文档必须不匹配这些条件才能被包含进来。
  • should:如果满足这些语句中的任意语句,将增加 _score ,否则,无任何影响。它们主要用于修正每个文档的相关性得分。
  • filter:必须匹配,但它以不评分、过滤模式来进行。这些语句对评分没有贡献,只是根据过滤标准来排除或包含文档。

排序

默认按照相关性降序排序
按照字段的值排序:

GET /_search
{
    "query" : {
        "bool" : {
            "filter" : { "term" : { "user_id" : 1 }}
        }
    },
    "sort": { "date": { "order": "desc" }}
}

此时返回的_score 和 max_score 字段都是 null
多级排序
匹配的结果首先按照日期排序,然后按照相关性排序:

GET /_search
{
    "query" : {
        .....
    },
    "sort": [
        { "date":   { "order": "desc" }},
        { "_score": { "order": "desc" }}
    ]
}

多级排序并不一定包含 _score 。你可以根据一些不同的字段进行排序,如地理距离或是脚本计算的特定值。
多值字段的排序
一种情形是字段有多个值的排序, 需要记住这些值并没有固有的顺序。对于数字或日期,你可以将多值字段减为单值,这可以通过使用 min 、max 、avg 或是sum排序模式 。例如你可以按照每个date字段中的最早日期进行排序。

"sort": {
    "dates": {
        "order": "asc",
        "mode":  "min"
    }
}

Elasticsearch 的相似度
Elasticsearch 的相似度算法被定义为检索词频率/反向文档频率, TF/IDF ,包括以下内容:
检索词频率
检索词在该字段出现的频率?出现频率越高,相关性也越高。 字段中出现过 5 次要比只出现过 1 次的相关性高。
反向文档频率
每个检索词在索引中出现的频率?频率越高,相关性越低。检索词出现在多数文档中会比出现在少数文档中的权重更低。
字段长度准则
字段的长度是多少?长度越长,相关性越低。 检索词出现在一个短的 title 要比同样的词出现在一个长的 content 字段权重更大。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值