一、elasticsearch的写一致性原理

主要是基于consistency来来进行操作的,其语法如下所示

put /index/_doc/id?consistency=quorum

one:要求我们这个写操作,只要有一个primary shard是active活跃可用的,就可以执行

all:要求我们这个写操作,必须所有的primary shard和replica shard都是活跃的,才可以执行这个写操作

quorum:默认的值,要求所有的shard中,必须是大部分的shard都是活跃的,可用的,才可以执行这个写操作

我们在发送任何一个增删改操作的时候,比如说put /index/type/id,都可以带上一个consistency参数,指明我们想要的写一致性是什么

1、quorum机制

quorum机制,写之前必须确保大多数shard都可用,int( (primary + number_of_replicas) / 2 ) + 1,当number_of_replicas>1时才生效

公式:

quroum = int( (primary + number_of_replicas) / 2 ) + 1

举个例子,4个primary shard,number_of_replicas=2,总共有4 + 4 * 2 = 12个shard,得出结果quorum = int( (4 + 2) / 2 ) + 1 = 4。所以,要求12个shard中至少有4个shard是active状态的,才可以执行这个写操作

如果节点数少于quorum数量,可能导致quorum不齐全,进而导致无法执行任何写操作,所以es提供了一种特殊的处理场景,就是说当number_of_replicas>1时才生效,因为假如说,你就一个primary shard,replica=1。此时就2个shard(1 + 1 / 2) + 1 = 2,要求必须有2个shard是活跃的,但是可能就1个node,此时就1个shard是活跃的,如果你不特殊处理的话,导致我们的单节点集群就无法工作..

举例说明如下:

1个primary shard,replica=3,quorum=((1 + 3) / 2) + 1 = 3,要求1个primary shard + 3个replica shard = 4个shard,其中必须有3个shard是要处于active状态的。如果这个时候只有2台机器的话,会出现什么情况呢?

答:

当quorum不齐全时,es会进行等待(wait)默认1分钟,等待期间,期望活跃的shard数量可以增加,最后实在不行,就会timeout,我们其实可以在写操作的时候,加一个timeout参数,比如说put /index/type/id?timeout=30,这个就是说自己去设定quorum不齐全的时候,es的timeout时长,可以缩短,也可以增长

二、搜索结果数据解析

在kibana或者是elasticsearch head 插件中发起查询请求:

GET /_search

其返回的结果如下:

{
  "took" : 1,
  "timed_out" : false,
  "_shards" : {
    "total" : 3,
    "successful" : 3,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : {
      "value" : 34,
      "relation" : "eq"
    },
    "max_score" : null,
    "hits" : [ ]
  }
}
  • took:整个搜索请求花费了多少毫秒
  • hits.total.value:本次搜索,返回了几条结果
  • hits.max_score:本次搜索的所有结果中,最大的相关度分数是多少,每一条document对于search的相关度,越相关,_score分数越大,排位越靠前
  • hits.hits:默认查询前10条数据,完整数据,_score降序排序
  • _shards:shards fail的条件(primary和replica全部挂掉),不影响其他shard。默认情况下来说,一个搜索请求,会打到一个index的所有primary shard上去,当然了,每个primary shard都可能会有一个或多个replic shard,所以请求也可以到primary shard的其中一个replica shard上去。
  • shards:total表示打到的全部分片,
  • shards:successful表示打到的分片中查询成功的分片,
  • shards:skipped表示打到的分片中跳过的分片,
  • shards:failed表示打到的分片中查询失败的分片
  • timeout:默认无timeout,latency平衡completeness,手动指定timeout,timeout查询执行机制

三、搜索的timeout机制底层的原理

默认情况下,es的timeout机制是关闭的。比如,如果你的搜索特别慢,每个shard都要花好几分钟才能查询出来所有的数据,那么你的搜索请求也会等待好几分钟才会返回。

但是我们有些应用系统对时间是非常敏感的,比如说电商网站,你不能让用户等5分钟才能等到一次搜索请求的结果。

timeout机制就可以指定每个shard只能在timeout时间范围内,将搜索到的部分数据(也可能是全部数据),直接返回给客户端,而不是等到所有的数据全部搜索出来以后再返回。确保一次搜索请求可以在用户指定的timeout时长内完成,为一些时间敏感的搜索应用提供良好的支持。

语法如下:

GET index/_search?timeout=10ms

四、multi-index和multi-type搜索模式

multi-index和multi-type搜索其实就是告诉你如何一次性搜索多个index和多个type下的数据

  • /_search:所有索引,所有type下的所有数据都搜索出来
  • /index1/_search:指定一个index,搜索其下所有type的数据
  • /index1,index2/_search:同时搜索两个index下的数据
  • /*1,*2/_search:按照通配符去匹配多个索引
  • /index1/type1/_search:搜索一个index下指定的type的数据
  • /_all/type1,type2/_search:_all,可以代表搜索所有index下的指定type的数据

五、分页搜索以及deep paging性能问题

1、如何使用es进行分页搜索

使用es的分页搜索,其核心是通过from和size关键字来实现的

from:从哪个角标开始搜索

size:共搜索多少条

举例说明如下:

假如product索引共有五条数据,其_id分别为12345,当我们进行分页搜索,每页显示两条数据,其返回结果如下

第一页:搜索出_id为4,5的数据

GET product/_search?from=0&size=2

es 内核 是 什么 语言开发的 es内部原理_数据库

第二页:搜索出_id为3,1的数据

GET product/_search?from=2&size=2

es 内核 是 什么 语言开发的 es内部原理_java_02

第三页:搜索出_id为2的数据

GET product/_search?from=4&size=2

es 内核 是 什么 语言开发的 es内部原理_java_03

注意:当数据超过5W条时,不建议from的方式进行分页查询,因为当数据大于5W时使用from分页数据会比较慢,推荐使用scroll游标的方式,在下文中我会详细讲解为啥要怎么做。

2、什么是deep paging性能问题?如何解决该问题呢?

(1)什么是deep paging性能问题

deep paging简单来说,就是搜索的特别深,比如index总共有60000条数据,存放在3个primary shard,每个shard上分20000条,每页是10条数据,这个时候你要搜索到第1000页,实际上要拿到的是10001-10010,该怎么拿呢?

请求首先可能是打到一个不包含这个index的shard的node上,这个node就是一个coordinate node(协调节点),这个coordinate node(协调节点)就会将搜索请求转发到index的三个shard所在的node上去。

要搜索60000条数据中的第1000页,实际上每个shard都要将内部的20000条数据中的第1-10010条数据拿出来,不是10条,是10010条数据,3个shard每个shard都返回10010条数据给coordinate node,coordinate node会收到总共30030条数据,然后根据_score(相关度分数)排序取到所需的那10条数据,其实就是我们要的最后的第1000页的10条数据。

举个例子,现在有100个带编号的乒乓球(从1到100),我现在随机给他们放到三个篮子里面(他们在篮子里面已经排好序了),现在我要取出第10-12个球,那我是不是应该先把各个篮子里面前12个球取出来放到一起(篮子里面的乒乓球是随机放的,无规律),共计36个球,然后汇总进行排序后,在这个结果中取出第10-12个乒乓球!!!

缺点

搜索过深的时候就需要在coordinate node上保存大量的数据,还要进行大量数据的排序,排序之后再取出对应的那一页,所以这个过程,既消耗网络宽带,耗费内存,还消耗cpu。这就是deep paging的性能问题,我们应该尽量避免出现这种deep paging操作。

(2)deep paging性能问题解决方案:使用scroll方式

为了解决上面的问题,elasticsearch提出了一个scroll滚动的方式,这个滚动的方式原理就是通过每次查询后,返回一个scroll_id。根据这个scroll_id 进行下一页的查询。可以把这个scroll_id理解为通常关系型数据库中的游标。但是,这种scroll方式的缺点是不能够进行反复查询,也就是说,只能进行下一页,不能进行上一页。

经过分析,如果数据达到了50000条以上,那么用户基本上是不会考虑每条都去看的,用户需要的是最后对数据分析处理后的结果。而如果小于50000条的时候我们可以使用from size的方式进行分页的查询。那么这种方式存在是为了什么情景呢。应该是为了分批次的检索所有数据。

实现步骤

a、以product索引举例说明。

首先取出前2条,并且得到scroll_id(这里的30s代表的是持续滚动时间,如果过了30秒钟,还没有查询下一页,那么这个scroll_id就会失效)。

GET product/_search?scroll=30s&size=2

返回结果

{
  "_scroll_id" : "DXF1ZXJ5QW5kRmV0Y2gBAAAAAAAfIdMWekdubkw4eF9TNS1GSW9UY1AxdzEtZw==",
  "took" : 0,
  "timed_out" : false,
  "_shards" : {
    "total" : 1,
    "successful" : 1,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : {
      "value" : 5,
      "relation" : "eq"
    },
    "max_score" : 1.0,
    "hits" : [
      {
        "_index" : "product",
        "_type" : "_doc",
        "_id" : "4",
        "_score" : 1.0,
        "_source" : {
          "id" : "4",
          "name" : "罗技 T60 鼠标",
          "desc" : "这是一个无线鼠标 T60",
          "price" : 25,
          "producer" : "罗技"
        }
      },
      {
        "_index" : "product",
        "_type" : "_doc",
        "_id" : "5",
        "_score" : 1.0,
        "_source" : {
          "id" : "5",
          "name" : "罗技 T501 鼠标",
          "desc" : "new 这是一个无线鼠标",
          "price" : 25,
          "producer" : "罗技"
        }
      }
    ]
  }
}

b、再次查询下一页

注意,这里查询时不需要指定index,只需要指定scroll_id和本次的持续滚动时间。

想要第几页,循环请求几次就行了,在设置的时间内scroll_id是不会变的

GET /_search/scroll?scroll=30s&scroll_id=DXF1ZXJ5QW5kRmV0Y2gBAAAAAAAfIdMWekdubkw4eF9TNS1GSW9UY1AxdzEtZw==

或者

POST /_search/scroll
{
 "scroll" : "30s",
 "scroll_id":"DXF1ZXJ5QW5kRmV0Y2gBAAAAAAAfIdMWekdubkw4eF9TNS1GSW9UY1AxdzEtZw=="
}

以上两种方式都可以使用,得出的结果也是一样的。

删除对应scroll_id

当我们搜索完毕或者说已经滚动到最后的时候,我们可以选择删除scroll_id

DELETE /_search/scroll/DXF1ZXJ5QW5kRmV0Y2gBAAAAAAAfIdMWekdubkw4eF9TNS1GSW9UY1AxdzEtZw=

删除所有scroll_id

DELETE /_search/scroll/_all

六、query string基础语法

一个是掌握q=field:search content的语法,还有一个是掌握+和-的含义,以下用两个例子来说明

查询product索引中name字段值为罗技的数据

GET product/_search?q=name:罗技

或者

GET product/_search?q=+name:罗技

这两个语法都会把product索引中name字段中包含罗技的所有数据给返回

es 内核 是 什么 语言开发的 es内部原理_java_04

查询product索引中name字段值不等于罗技的数据

GET product/_search?q=-name:罗技

es 内核 是 什么 语言开发的 es内部原理_java_05

七、_all metadata的原理和作用

GET /product/_doc/_search?q=罗技

直接可以搜索所有的field,任意一个field包含指定的关键字就可以搜索出来。我们在进行中搜索的时候,难道是对document中的每一个field都进行一次搜索吗?其实并不是的,是通过es中的_all元数据进行搜索的。

es中的_all元数据,在建立索引的时候,我们插入一条document,它里面包含了多个field,此时,es会自动将多个field的值,全部用字符串的方式串联起来,变成一个长的字符串,作为_all field的值,同时建立索引

后面如果在搜索的时候,没有对某个field指定搜索,就默认搜索_all field,其中是包含了所有field的值的

举个例子

{
    "id":"1",
    "name":"清风纸巾",
    "desc":"这是一个纸巾",
    "price":25,
    "producer":"清风"
}

"1 清风纸巾 25 清风",作为这一条document的_all field的值,同时进行分词后建立对应的倒排索引

ps:es6.x默认已禁用全文索引(_all),es7.0 彻底废弃_all字段支持,为提升性能默认不再支持全文检索,即7.0之后版本进行该项配置会报错。