我说2w字可以入门ES,非但不信还打我

今天距离过年还剩一个月的时间,小伙伴们是否已经开始学习新技术为年后找工作开始做准备了呢。Freemen为各位小伙伴转载一些必备知识点,希望小伙伴们可以收获满满,Freemen APP 也在时刻为大家年后工作准备者,有需要招聘或者找工作的不妨去Freemen上浏览下。

一、概述

Elasticsearch是一个基于Apache Lucene(TM)的分布式、高扩展、高实时、RESTful 风格的搜索和数据分析引擎。Elasticsearch是用Java语言开发的,并作为Apache许可条款下的开放源码发布,是一种流行的企业级搜索引擎。Elasticsearch可用于云计算中,能够达到实时搜索,稳定,可靠,快速,安装使用方便。官方客户端在Java、.NET(C#)、PHP、Python、ApacheGroovy、Ruby和许多其他语言中都是可用的。根据DB-Engines的排名显示,Elasticsearch是最受欢迎的企业搜索引擎,其次是Apache Solr,也是基于Lucene。它能很方便的使大量数据具有搜索、分析和探索的能力。充分利用Elasticsearch的水平伸缩性,能使数据在生产环境变得更有价值。

Elasticsearch 的实现原理主要分为以下几个步骤,首先用户将数据提交到Elasticsearch数据库中,再通过分词控制器去将对应的语句分词,将其权重和分词结果一并存入数据,当用户搜索数据时候,再根据权重将结果排名,打分,再将返回结果呈现给用户。


二、安装

2.1、JDK安装

关于Jdk的安装步骤这里省略掉,只需要下载软件包配置环境变量即可。

2.2、下载软件包并解压缩

# 1.下载es


wget  https://artifacts.elastic.co/downloads/elasticsearch/elasticsearch-5.6.9.tar.gz


# 2.解压缩


tar -zxvf elasticsearch-5.6.9.tar.gz

2.3、修改配置文件

2.3.1、修改elasticsearch.yml文件

cd elasticsearch-5.6.9/config
vim elasticsearch.yml
 
cluster.name: #集群的名称(例如:es,每个节点的cluster.name的配置应保持一致)
node.name: #节点的名称(每个节点的名称原则上不同的,例如每台机器分别是es1,es2,es3,es4,es5)
bootstrap.memory_lock: false
bootstrap.system_call_filter: false
network.host: #局域网ip(或者0.0.0.0,如果你的机器上安装了docker之类的,会出现0.0.0.0被docker占用,那么这个节点就不会被集群发现,因为集群默认是根据0.0.0.0来发现节点,此时就修改此处为局域网的ip)
http.port: http访问端口(默认是9200,建议修改)
discovery.zen.ping.unicast.hosts: ["192.17.92.002", "192.17.92.003", "192.17.92.004", "192.17.92.005"] #此处是为了集群互相发现节点,此处hosts列表配置除了此节点的ip的其他ip,其他节点的此处配置也是同样操作
http.cors.enabled: true #head插件发现节点
http.cors.allow-origin: "*" #head插件发现节点
http.cors.allow-headers: Authorization,X-Requested-With,Content-Length,Content-Type
action.auto_create_index: .security,.security-6,.monitoring*,.watches,.triggered_watches,.watcher-history*,app-a-*,app-b-*,.ml*

2.3.2、修改/etc/security/limits.conf

vim /etc/security/limits.conf
#修改下面几项,没有的就添加
* soft nofile 65536
* hard nofile 131072
* soft nproc 2048
* hard nproc 4096

2.3.3、修改/etc/sysctl.conf,添加下面一行

vim /etc/sysctl.conf
# 添加下面一行
vm.max_map_count=262144

2.4、启动服务

cd elasticsearch-5.6.9/bin
./elasticsearch -d

2.5、测试服务

浏览器访问ip:port(port就是刚才配置文件里的http.port),出现如下信息表示安装成功

{
  "name" : "es",
  "cluster_name" :"es1",
  "cluster_uuid" :"1XPQsLk1TDOjkfHmwytD7w",
  "version" : {
    "number" :"5.6.9",
    "build_hash" :"877a590",
    "build_date" :"2018-04-12T16:25:14.838Z",
    "build_snapshot" :false,
    "lucene_version" :"6.6.1"
  },
  "tagline" : "YouKnow, for Search"
}

2.6、安装head插件

head插件是一种可以查询es数据的可视化工具,类似于Kibana,Cerebro。

2.6.1、环境准备

curl -sL https://rpm.nodesource.com/setup_8.x | bash -
sudo yum install gcc-c++ make
curl -sL https://dl.yarnpkg.com/rpm/yarn.repo | sudo tee /etc/yum.repos.d/yarn.repo
yum -y install yarn nodejs git
npm install -g grunt-cli
cd /home/elastic  #在elasticsearch的同级目录下
#clone head插件
git clone git://github.com/mobz/elasticsearch-head.git

2.6.2、修改Gruntfile.js

cd /home/elasticelasticsearch-head/head
vim Gruntfile.js
#修改下面这一段:
        connect: {
          server: {
          options: {
            port: 9100,
            hostname: '*', #添加hostname这一行
            base: '.',
            keepalive: true
            }
          }
        }

2.6.3、修改app.js

cd /home/elastic/elasticsearch-head/_site
vim app.js
#修改下面这一段,就是修改ip:port,
#如果使用的是通过公网ip来访问,此处就写公网ip,否则就写局域网ip,port是elasticsearch配置文件的http.port
this.base_uri = this.config.base_uri ||this.prefs.get("app-base_uri") || "http://ip:port";

2.6.4、 install grunt

npm install grunt --save-dev
npm install

2.6.5、启动head服务

nohup grunt server &

2.6.6、测试服务

打开浏览器,访问http://ip:9100

三、核心概念

3.1、Index

index通常我们称之为索引,一个索引是一个逻辑命名空间,可以理解成数据库。它有一个包含类型(type)的映射,该类型(type)包含索引中的字段(field)。索引会映射到一个或多个主分片上,分片上可以有零个或多个副本分片。通俗来说,索引里面包含一堆结构相似的数据,而且每个索引都会对应一个名称。

3.2、type

type称为类型,一个用于表示文档的类型,可以理解成数据库中的表,例如:电子邮件、用户或推特。每个索引里可以有一个或多个type.type是index中的一个逻辑数据分类。当然在es6之后的版本中type会被废弃掉。

我们把type类比为数据库中的表,其实是不恰当的。在 SQL 数据库中,表是相互独立的。一个表中的列与另一表中同名的列没有关系。但对于映射类型中的字段并不是这样的,在 es索引中,不同映射类型的同名字段在内部由同一个 Lucene 字段支持。举个例子,在es中有一个名为user的类型,其中有一个user_name字段;同时也有一个address的类型,也有同样的一个user_name字段,那么这两个user_name字段在两个type中必须要有相同的定义映射。

除此之外,在同一个索引中存储很少或没有共同字段的不同实体,会导致数据稀疏,并干扰Lucene有效压缩文档的能力。所以基于这些情况,es社区决定将在索引中删除这个映射类型的概念。换句话理解就是一个index里面就只有一个type了。

3.3、document

文档是以JSON形式存储在 Elasticsearch 中的,其是最小的数据单元,可以类比为数据表中的一条记录。每个索引下可以存储多个document,而且每一个document都会有一个id,可以自动生成或者用户指定。一个document中有多个field,每个field就是一个数据字段。

一个文档是一个JSON对象(在其他语言中也被称为hash/hashmap/associative array),它包含零或多个字段或者键值对。被索引的原始JSON文档将被存储在_source字段中,当获取或搜索一个文档时,默认会返回该字段

3.4、id

该id对应的是document的唯一标识符,如果没有指定则会自动生成。

3.5、field

document里包含一个或多个field,field可以类比为数据表中的一个字段,其field对应的值可以是基本类型的(如int,string,date),也可以是结构化类型的(如nested struct,array或者是一个对象)。所以每个field都有其自己的类型

3.6、Cluster

集群包含一个或多个node节点,并且每个节点都有一个相同的集群名便于自动检测加入同一个集群中,每个集群都有一个主节点,由集群自动选择,如果当前主节点出现故障,可以替换该主节点。

3.7、Shard

从前面对es的介绍可以知道es是分布式的,也就是说单台机器是无法存储大量数据的,那如果一个索引下有大量的数据,那么就需要进行切分,那么切分后得到的就是shard(分片),然后分布在多台服务器上进行存储。其实每个shard都是一个luence index,但是单个 Lucene 索引中可以包含的文档数量有上限。从 LUCENE-5843 开始,限制为 2,147,483,519 (=Integer.MAX_VALUE - 128) 个文档。

shard又分为primary shard(主分片)和replicashard(副本分片),每个document(文档)都有一个主分片并在其上。当你要索引一个文档时,它首先在主分片上建立索引,然后在主分片的所有副本上建立索引。

默认情况下,一个索引有 5 个主分片。当然还可以指定更少或更多的主分片来扩展索引可以处理的文档数量。

需要注意的是:一旦创建了索引,就无法更改索引中的主分片数量

3.8、Replica

每一个主分片都有零个或多个副本分片(replica shard)。副本分片的作用主要有两个:

默认情况下,每个主分片都有一个副本,但可以在现有索引上动态更改副本数量。副本分片永远不会在与其主分片相同的节点上启动

3.9、mapping

mapping,也会被叫做映射。可以类比作关系数据库中的schema定义,每一个索引都有一个mapping,其中包括了定义了type,以及其他一些索引的设置。映射既可以明确定义,也可以在索引文档时自动生成

3.10、routing

当你索引一个document的时候,它会被存储到一个主分片上,那么这个分片的选择是通过hash路由的规则来决定的。默认情况下,这个路由值是经过document id计算得到的,如果一个document有一个指定的父document,那么就取父document id,这样的话就可以确保父子document都会被存储到同一个分片上。

3.11、NRT(Near Realtime)

es其实是一个近实时的搜索引擎,很多人会被其宣传语误导。也就是说从索引文档到可搜索之间存在轻微的延迟(通常为一秒),下面的文章中会介绍为什么是1s,以及为什么是近实时

3.12、Segments

es索引分片在实际存储的时候会被细分为更小的单元,称为segement(段),段是文件系统中的结构,在数据进行索引时会被创建,而我们平时的搜索操作其实底层是通过segment这一层进行的。一旦segement被创建之后内容就不能被修改,如果要删除一个文件的时候就只需要标记为删除即可,同样的,对于更新操作,要先删掉旧的文件,然后再创建一个新的文件。

当然上述的标记操作会在后台执行真正的删除操作来释放存储空间,该操作通常是在合并阶段进行的,而合并也有助于将小而大量的segment合并成一个较大的segment。当然过多小量的segment也会影响搜索性能,可以类比于HDFS小文件的危害。

3.13、Translog

当客户端将数据写入索引的时候,其实在节点上并还没有落盘,而是先存储到了内存中,当达到了一定的阈值后,才会提交到磁盘,这样也避免了频繁写入磁盘而产生大量的小分片。但这里就会有一些数据丢失的风险,特别是当节点故障或者断电的时候,那么在内存中的数据就会丢失,为了避免这一情况的发生,我们在写入数据的时候,同时会写到一个事务日志中,那么这个就称之为translog,如果发生故障的时候,那么就可以从translog中恢复重放数据。

当数据写入内存后也不是立即就能被搜索到的,除非执行刷新操作(默认是一秒一次),那么会将在内存中的数据刷新到系统缓存中,这个时候数据才是可见的。通常情况下是会一直往事务日志中写的,那么随着时间推移,日志量也会越来越大,当达到一定阈值后日志会进行归档。所以可以在索引级别设置translog的大小以及同步间隔。

更加详细的流程可见下图。

四、整体架构

上图为整个ES的详细流程交互图,es集群不像hadoop集群一样,es集群中的每个节点是对等的,也就是不存在主从之分,每个节点的职责都是一样的,都可以接受客户端的请求来处理。所以es是可以通过横向扩展来实现线性增加容量,同时也会保持相似的性能。

虽然每个节点都是对等的,都可以执行任何操作,但真的如果这么做可能会对集群的稳定性产生一些负面问题,所以一般标准做法还是为不同的集群功能指定不同的节点集。

如上图所示,es集群将节点分为了四种角色,对于集群来说,无论在什么时候都要有一个主节点,来维护集群的健康和监测集群的状态,这就是Master Node。该类角色通常是比较轻量级的,因为它们不会去处理任何数据,其主要作为协调者,来负责创建、删除、管理索引,并将索引和底层的碎片分配给集群中的数据节点。

刚才提到集群中要有一个主节点,那么考虑到容错性的话,就需要具备一些候选节点来作为主节点的备用,这样当主节点出现故障的时候,候选节点就可以成为主节点,继续提供服务。对于这种类型的节点角色称之为Master-Eligible。一个集群中建议部署奇数个master-eligible来处理集群分区。

在集群中,真正存储数据的节点就称为Data Node,该种角色的职责主要用来保存实际的索引数据并处理数据的etl和聚合。该类节点通常是属于CPU和内存密集型的,需要足够多的资源来处理数据。因此es集群在扩容的时候通常是采用横向扩展,而不是纵向扩展。

在数仓的整合流程中,有一道ETL的工序,那么在es中也可以实现数据被提取之前进行预处理的操作,做这类工作的角色称之为摄取节点(Ingest Node),该类节点可能是资源密集型的,而且为这种提取管道独立出对应的节点也是能够提升性能的,而且搜索操作也不会影响提取流程

4.1、写流程剖析

我们先来介绍下精简版的写入流程:

1、客户端向ES集群发起写入请求,这个时候呢,会和一个node节点进行连接,那么这个节点就称为协调节点(coordinating node)

2、当协调节点接收到请求后,会根据document对应的ID进行路由计算,然后将请求转发到对应的节点上。

3、当节点收到协调节点发来的写入通知后,就会将该document写入到本地,那么该document在写入节点对应的shard分片就是主分片(primary shard)

4、当主分片写入完成之后呢,会随机找一台机器进行副本复制,写入replica shard。

5、当主备分片都写入完成之后,协调节点就会将结果返回给客户端。

以上就是比较浅显的介绍,更加细节的流程如下(详细流程图见该章节的首图):

我们拿写一条数据为例来详细剖析下写入过程:

1、客户端连接到es集群中的某个节点上,发起写请求,那么该节点就是协调节点。

2、协调节点根据写入的document对应的id进行路由计算,然后和具体存储数据的节点进行通信,发送数据

3、数据节点将写入数据首先会写到应用内存缓存区中,同时为了避免数据丢失,也会将操作记录写入到translog buffer中

4、当内存缓存区达到了一定阈值(默认是1s刷新一次,或者容量达到了阈值),那么就会将内存中的数据刷写到系统内存缓存区中,这个时候数据才能对外可见,也就是说只有等到数据进入了系统缓存区之后,客户端才能查询到该条数据。

5、随着时间的推移,系统缓存的容量也会达到一定的阈值,这个时候会进行flush操作,将数据进行持久化落盘(当然这里除了容量阈值外,每隔30min也会触发,translog文件归档也会发生触发)。

6、数据落盘后,底层其实是以segment单元进行存储的,同时也会提交point来标识目前所有可用的segment 文件。

7、在持续写入的过程中,translog buffer的容量也会越来越少,默认是5s以异步的方式刷写到磁盘中(当然这个间隔可以通过index.translog.sync_interval参数来调整)。

8、当translog落盘之后,文件积累个数也会越来越多,所以需要一定的机制自动清理,es会每隔30min或者文件大小达到了阈值,就会触发refresh操作,这个时候就会把历史的文件给删掉,同时也会将缓冲区的记录刷写到磁盘生成新的文件。

9、在进行refresh的时候,同样也会触发对segment的合并,这个时候会将标记删除的document进行彻底删除。

10、当数据成功写入到主分片上后,就会进行副本分片的复制,副本分片的流程同主分片是一样的。

4.2、读流程剖析

我们先来介绍下精简版的读取流程。

1、客户端向集群发起读取请求,举例来说只是简单查询,然后就会和连接的节点进行通信,那么该节点就是协调节点。

2、协调节点根据查询的条件进行hash路由,按照轮询的方式来查找符合条件的主分片。

3、然后存储真正数据的节点将数据返回给协调节点进行汇总后再返回给客户端,至此简单的读取流程就结束了。当然如果主分片所在的节点发生了读取失败,那么就会从副本分片中进行拉取。

五、基本操作

5.1、Index操作

PUT /test_index?pretty #创建索引
DELETE /test_index?pretty #删除索引
DELETE /index_one,index_two # 删除索引index_one,index_two
DELETE /index_* # 删除以index开头的索引
DELETE /_all # 删除所有索引
GET /test_index/_settings #查看索引配置

5.1.1、Create Index

5.1.1.1、创建索引的时候指定相关设置

PUT twitter
{
    "settings" : {
        "index" : {
           "number_of_shards" : 3, # 指定主分片的个数,设置后不能再调整了,默认number_of_shards=5
           "number_of_replicas" : 2 #指定每个主分片对应的副分片的个数,默认number_of_replicas=1
        }
    }
}
 
# 简化版本
PUT twitter
{
    "settings" : {
        "number_of_shards": 3,
        "number_of_replicas" : 2
    }
   }

5.1.1.2、创建索引的时候指定mapping

PUT test
{
    "settings" : {
        "number_of_shards": 1 #设置主分片个数
    },
    "mappings" : {
        "type1" : {
            "properties" :{
                "field1" :{ "type" : "text" } # 设置对应的字段和字段类型
            }
        }
    }
}

5.1.1.3、创建索引的时候指定别名

一个索引上可以指定多个别名,每个别名就可以理解为一张"视图"

PUT test
{
    "aliases" : {
        "alias_1" : {},
        "alias_2" : {
            "filter" : {
                "term" :{"user" : "kimchy" }
            },
            "routing" :"kimchy" # 指定路由,也就是说当使用别名搜索的时候,都会按照该路由值进行路由查询
        }
    }

5.1.1.4、创建索引响应结果分析

默认情况下,当创建一个索引的时候,客户端会等到主副分片开始创建的时候或者超时的情况下才会收到响应,当创建完索引后会返回如下JSON结构体结果

{
    "acknowledged": true,
    "shards_acknowledged":true,
    "index":"test"
}

acknowledged表示索引是否在集群中创建成功。

shards_acknowledged表示是否在超时之前为索引中的每个分片启动指定数量的副本分片。

需要注意的是:即使索引创建成功,acknowledged和shards_acknowledged的值也有可能为false,因为有些情况下索引很快就会创建好了,但是在更新集群状态或者分片复制的时候发生了超时。

我们可以配置"index.write.wait_for_active_shards"参数来调整只等待主分片启动的默认值(需要注意的是:调整该参数值也会影响后续所有写操作的wait_for_active_shards值)

PUT test
{
    "settings": {
        "index.write.wait_for_active_shards":"2"
    }
}
# 简化版本
PUT test?wait_for_active_shards=2

5.1.2、Delete Index

当你想要删除一个索引的时候,那么数据也会被全部删除,所以该操作需要慎重使用。注意:不能使用别名来删除索引

DELETE /twitter

如果你有要删除多个索引的场景,那么也可以使用通配符的方式进行匹配。但是该操作也可以在服务端通过配置action.destructive_requires_name设置为true来禁用

5.1.3、Get Index

如果想要查看索引的下的数据,可以使用Get方式,下方是获取一个索引的数据,当然也可以使用通配符的方式来获取多个索引的数据

GET /twitter

5.1.4、 Index Exists

如果要判断一个索引是否存在,可以通过使用下面的命令来验证。返回的结果中包含HTTP 状态值,如果值为200则表示存在;如果值为404则表示不存在

HEAD twitter

5.1.5、Index Stats

该命令提供了索引级别的统计信息

GET /_stats #可以查看所有索引的统计信息
GET /index1,index2/_stats # 可以查看index1,index2索引的统计信息

5.1.6、IndicesSegments

该命令可以查看索引下的segements情况

GET /test/_segments

5.1.7、Index Refresh

我们可以对一个或多个索引执行refresh命令,即让上次刷新之后的在内存中的数据可搜索,默认情况下会定期刷新的

POST /twitter/_refresh #对twitter索引进行刷新
POST /kimchy,elasticsearch/_refresh # 对kimchy,elasticsearch两个索引进行刷新
POST /_refresh #对集群中的所有索引都进行刷新

5.1.8、Index Merge

该命令的合并操作应用于索引级别,即将索引下的segment进行合并,这样做为了减少segment个数,提升搜索性能。该命令调用在完成结束之前会处于阻塞状态。如果http连接断开了,那么合并动作就会在后台进行,而且其他新的请求也会被阻塞住。所以该操作一般在业务低峰期进行。

POST /twitter/_forcemerge

在强制合并的时候,我们也可以指定一些合并参数。例如设置max_num_segments可调整需要合并的最大个数

5.2、document操作

对于document的操作,通常我们使用比较多的是查询场景,而且es为我们提供了一种基于JSON结构定义的查询语言(Query DSL)。主要包括两类不同的查询语义:

叶子查询法就是在指定的字段中搜索指定的值,实现该种方式的关键词有match,term,range。

复合查询法会包含叶子查询或者多种复合查询,其能够实现多重查询,比如bool 或者dis_max

5.2.1、常用关键词

# 常用关键词
"query": 在请求消息体中的query允许我们用Query DSL的方式查询。
"term": 查询时判断某个document是否包含某个具体的值,不会对被查询的值进行分词查询,代表精准匹配,不会对搜索词进行分词拆解;如果要搜索多个词,使用terms关键词,其中terms里的[]多个是或者关系,只要满足其中一个词就可以,如果要满足两个词的话,要使用bool里的must进行匹配
         {
                  "query":{
                  "terms":{
                          "title": ["love","China"]
                  }
                   }
         }
"match" 将被查询值进行分词,然后用评分机制(TF/IDF)进行打分
"match_phrase": 短语搜索,要求所有的分词必须同时出现在文档中,且位置必须紧邻一致
"match_phrase_prefix":原理其实跟match_phrase类似,区别在于把最后一个字母term作为前缀去搜索,使用max_expansions参数限制前缀扩展的影响,可以控制与前缀匹配的词的数量,当匹配的doc数量超过max_expansions时就会结束;例如输入hello w时会出现多个hello w的词,底层是将hello进行match操作,w作为前缀进行term操作,然后将两个搜索结果进行关联,且位置也必须紧邻一致的词条才会输出
"wildcard":支持通配符的模糊查询,?匹配单个字符,*匹配任何字符;prefix query 不计算相关度分数,与prefix filter唯一的区别就是filter会cache bitset,前缀越短,要处理的doc越多,性能越差,尽可能使用长前缀来搜索
"regexp":正则匹配
"Bool": 结合其他真值查询,通常和must、should、mustnot(与或非)一起组合出复杂的查询
"range": 查询时指定某个字段在某个特定的范围
"from": 以一定的偏移量来查看我们检索的结果,缺省从检索的第一条数据开始显示
"size": 指定检索结果中输出的数据条数,缺省为10条
"sort": 允许我们将检索的结果以指定的字段进行排序显示
"_source": 指定检索结果输出的字段
"aggs": 基于搜索查询,可以嵌套聚合来组合复杂的需求
     "aggs": {
        "NAME": {# 指定结果的名称
                "AGG_TYPE": {# 指定具体的聚合方法,
                        TODO: # 聚合体内制定具体的聚合字段
                      }
            }
        TODO: # 该处可以嵌套聚合
      }

5.2.2、term、match、match_phrase三者对比

term:该类关键词用于精准匹配,不会对指定的搜索值进行分词处理。也就是说该类关键词不会进行分词处理,属于词项搜索,例如如果要搜索“china”词,那么就会返回china,而不是返回chain这类的词;

match:该类关键词用于模糊匹配,会对查询值进行分词处理,返回的结果值会根据TF/IDF进行打分并按照分值降序输出

match_phrase:该类用于短语搜索,要求所有的分词必须同时出现在文档中,而且位置必须紧邻一致。也就是说该关键词会进行分词,属于全文搜索,但分词位置必须相邻

5.2.3、Query Andfilter context

5.2.3.1、Query Context

查询上下文,在这种上下文环境中,查询语句的返回的结果是”结果和查询语句的匹配程序如何“,返回的结果数据中都会带上_score值,象征匹配程度

5.2.3.2、Filter Context

过滤上下文,在这种上下文环境中,查询语句则表示匹配与否(yes or no)。例如要查询timestamp是否在2020~2021年之间;status值是否为"on"等等具有过滤性的语境。

通常只需要在查询子句中传递给过滤器一些参数,那么对应的过滤器上下文就会生效。例如在bool查询中增加filter或者must_not参数。

示例:

GET /_search
{
  "query": {  # 查询上下文
    "bool": {
      "must": [
        { "match": {"title":  "Search"        }},  #查询上下文
        { "match": {"content": "Elasticsearch" }}   #查询上下文
      ],
      "filter": [  #过滤上下文
        { "term":  { "status": "published"}},
        { "range": {"publish_date": { "gte": "2015-01-01" }}}
      ]
    }
  }
}

注意:es内置式为filter context保留缓存用来提高查询性能,因此filter context 查询的速度要快于query context。

5.2.3.3、Match All Query

通常我们在实际查询中,使用比较简单的查询所有的document,类似于select * from table。然后返回给我们带有分数值_score的结果

GET /_search
{
    "query": {
        "match_all": {}
    }
}

当然这个分数权重,我们是可以调整的。

GET /_search
{
    "query": {
        "match_all": {"boost" : 1.2 }
    }
}

5.2.3.4、Match Query

match查询接收text/numberics/dates,并根据搜索值进行分词处理。例如查询index索引中message字段包含“this is a test”的文档(document):

GET /index/_search
{
    "query": {
        "match" : {
            "message" :"this is a test"
        }
    }
}

5.2.3.5、Term Query

termquery(术语查询)查找包含倒排索引中指定的确定的文档。例如:

GET /index/_search
{
  "query":{
         "term":{
               "user":"kimchy"
         }
  }
}

如上面的demo,在user字段的倒排索引中查找包含kimchy的文档

也可以通过指定boost参数来给这个词查询设置相关的评分权重

GET /index/_search
{
  "query": {
    "bool": {
      "should": [
        {
          "term": {
            "status": {
              "value":"urgent",
                          "boost": 2.0 
            }
          }
        },
        {
          "term": {
            "status": "normal" 
          }
        }
      ]
    }
  }
}

如上述demo,设置status=urgent对应的boost值为2,意味着它有2倍的提升,那么_score的得分会比普通的要高。默认boost的值为1.0。这里boost参数的含义是用来提升查询语句相对权重,如果要降低权重,则设置值范围在0~1,如果要提升相对权重,则设置值范围大于1。需要知道的是,该值并不会线性提升查询权重,如该示例,设置boost=2,并不一定得到的权重分数值是普通的2倍。实际的权重值是经过归一化和一些其他内部的优化过程。所以记住boost参数只是影响相关度评分的一个因子

5.2.3.6、Range Query

当我们想要筛选符合范围的一些document时候,可以使用range。Lucene查询的类型取决于字段类型,对于字符串类型采用的是TermRangeQuery,对于数字/日期类型,查询的是NumericRangeQuery。举例想要查询年龄符合10~20之间的所有文档

GET _search
{
    "query": {
        "range" : {
            "age" : {
                "gte" :10,
            "lte" : 20,
            "boost" :2.0
            }
        }
    }
}

5.2.4、mget批量查询

通过docs来指定多个条件进行批量查询

GET /_mget
{
  "docs":[
            {
             "_index":"test_index",
              "_id":1
            },
            {
             "_index":"test_index",
              "_id":2
            }
    ]
}

5.2.5、bulk

语法如下

post /_bulk
{
   "action":{"metadata"}
}
{
    "data"
 }

其中action操作包括:delete、create、index、update。

delete:删除一个文档

create:创建一个文档

index:普通的Put操作,也可以创建文档,也可以是全量替换文档

update:执行update操作。注意:create和index和update操作后面紧跟着data,即具体的数据。

示例:

POST /_bulk
{"delete":{"_index":"test_index","_type":"test_type","_id":1}}
{"create":{"_index":"test_index","_type":"test_type","_id":1}}
{"test_field":"bulk create field"}
{"index":{"_index":"test_index","_type":"test_type","_id":6}}
{"test_field":"bulk index create field"}
{"update":{"_index":"test_index","_type":"test_type","_id":2}}
{"doc":{"test_field":"bulk update id=2"}}

bulk语法对json有着严格的要求,每个json串不能换行,也就是说同一个json串只能放一行,同时json串之间必须要有一个换行。

需要注意的是:Bulk操作中任意一个发生了失败,也不会影响其他操作的执行。bulk请求会加载到内存中,所以请求大小也要控制好,如果太大的话,性能反而会下降,一般控制在5MB~15MB之间。

5.3、Cat操作

5.3.1、查看集群存在哪些索引

GET _cat/indices?v

5.3.2、查看每个节点分配情况

GET /_cat/allocation?v

5.3.3、查看集群整体健康情况

GET /_cat/health?v
# 健康情况分为red、yellow、green

5.3.4、查看每个节点fielddata内存占用情况

GET /_cat/fielddata?v

5.3.5、查看每个索引的具体信息

GET /_cat/indices?v

5.3.6、查看所有索引的document数量

GET /_cat/count?v

5.3.7、查看master node当前的具体情况

使用该命令可以查看具体哪台节点是处于master角色

GET /_cat/master?v

5.3.8、查看每个Node的具体情况

使用该命令可以查看每一台节点的具体信息,比如JVM、负载等情况

GET /_cat/nodes?v

5.3.9、查看每个分片的具体情况

使用该命令可以查看每一个分片的具体情况,比如存储的ip,分片的类型等信息。

GET /_cat/shards?v

5.3.10、查看当前pending没执行完的task具体情况

使用该命令可以查看当前处于pending中的具体任务情况

GET /_cat/pending_tasks?v


5.3.11、查看分片恢复的过程情况

GET _cat/recovery?v

5.3.12、查看每个线程池的具体情况

通过使用该命令可以查看到每种线程的运行的情况

GET /_cat/thread_pool?v

5.3.13、查看每个segment的情况

下面命令可以查看每个segment在哪个服务器上,有多少个document,有多少数据可搜索等情况

GET /_cat/segments?v


5.3.14、查看当前有多少个template,以及具体情况

GET /_cat/templates?v&s=name

六、核心元数据

我们先不来探讨什么是元数据,先来看如下图,在我们使用搜索查询的时候,每个document都会附带有以下几块信息。那么这几块信息就称为元数据。接下来分别介绍下字段含义


6.1、_index

该字段值代表的是这个document是属于哪个index索引上的。而且索引名必须是小写,不能用下划线开头的,不能包含逗号

6.2、_type

该字段值代表的是文档的mapping类型。

6.3、_id

该字段值代表的是document的ID,可以理解为主键标识,该值默认是自动生成的,长度为20个字符,BASE64编码;也可以用户指定,在分布式系统中并行生成时不会发生冲突。

6.4、_score

该字段值代表的是相关度评分

6.5、_source

该字段下的值是真正document所附带的信息,一系列kv对。

七、字段类型

在document中的文档字段中,Es支持以下数据类型:

7.1、基本数据类型

es中以text和keyword两种类型来代表String的字符串。

数字类型包括:long、integer、short、byte、double、float、half_float、scaled_float

日期类型包括:date

布尔类型包括:boolean

二进制类型包括:binary

范围类型包括:integer_range、float_range、long_range、double_range、date_range

7.2、复杂数据类型

es使用object类型来表示JSON对象;es使用nested类型来表示JSON数组的结构

7.3、Geo数据类型

es中使用geo_point类型来表示经纬度;使用geo_shape来表示复杂形状,如多边形

7.4、专用数据类型

es中使用ip来表示ipv4或者ipv6地址;使用completion来提供自动建议;使用token_count来计算字符串中的标记数;使用murmur3来计算hash值并将该值存储到索引中;使用join类型来为同一个索引中的document定义父子关系。

7.5、数组

在es中,数组不需要专用的字段数据类型,默认情况下,任何字段可以包含零个或多个值,但是数组的所有值都必须要有相同的数据类型

八、倒排/正向索引

8.1、倒排索引概念

倒排索引,也常被称为反向索引,相对立也有一个正向索引,我们后面再说。倒向索引被用来存储在全文搜索下某个单词在一个文档或者一组文档中的存储位置的映射,通过倒排索引,可以根据单词快速获取包含这个单词的文档列表。倒排索引主要由两部分组成:"单词词典"和"倒排文件"。是不是听起来有点迷糊?没关系,看下图:

我们拿小时候学习用到的新华字典为例子,在小时候我们要查找某个词的拼音或者常用语的时候,首先是要根据偏旁部首找到对应的页码,然后再在具体的页码中找到对应的词,这样一番操作就是我们要介绍的倒排索引。如下,当我要搜索"奶茶"关键词的时候,就会准备的返还给我具体某个店的哪个品类的奶茶,响应速度非常快,这里就用到了倒排索引,那底层具体是怎么存储运行的呢?

如上图所示,底层会根据分词器进行分词,然后进行倒排索引存储,当用户搜索的时候,会先进行分词解析匹配然后定位到具体的ID,然后再根据ID查找具体的信息(如门店,具体的饮品)。这就是倒排索引大概的流程和用途了。

8.2、正向索引概念

和倒排索引相对立的就是正向索引,正向索引又被称为"向前索引",是创建倒排索引的基础,在索引创建的时候会自动生成并落盘的。那么怎么理解正向索引呢?举个例子:一本书总会带有目录页和索引页的,目录页方便读者从整体了解书中有哪些知识,索引页是一些关键词的介绍,比如引用,注明、出现在哪个章节等等。那么目录页就可以理解成正向索引,而索引页就可以理解成前面介绍到的倒排索引。正排索引是根据文档id查找到具体的文档内容。

九、集群发现

es的集群部署方式和第二节介绍的部署是一样的,只不过在集群模式下有一种模式需要大家了解的,即自动发现模式。

9.1、zen discovery集群自动发现机制

在前面介绍到es是属于p2p点对点的系统架构,集群中的每个节点是可以直接跟其他节点进行通信的,大家是相互平等的,而不是像Hadoop系统那样的Master-Slave架构。那么这里就有一个问题了,es节点之间是如何发现对方并进行通信的呢?这里其实就是es中的zen discovery机制,当然该机制也负责集群的master节点选举。

默认情况下,es会绑定在自己的回环地址上,也就是127.0.0.1地址,然后呢再扫描本地上的9300-9305端口,尝试跟这些端口上启动的其他es进程进行通信,然后组成一个集群,当然这种方式是单机版的。

zendiscovery机制是es内置的默认发现模块,它提供了unicast(单播)和基于文件的发现,可以通过插件扩展来支持云环境和其他形式的发现;当然zen discovery和其他模块集成,比如节点之间的所有通信都使用的transport模块来完成。

zendiscovery机制这里有几个子模块,分别来介绍下:

9.1.1、Ping

某个节点通过zen discovery机制来找到其他的节点是通过ping来实现的。

9.1.2、Seed nodes

zendiscovery机制通过使用种子列表来启动发现过程,在启动的过程中或者在选举master的时候,es会尝试连接到列表中的每个种子节点,并和它们进行通信,以此来查询其他节点并构建整个集群成员图。

默认情况下,有两种方式可以配置种子节点列表:unicast(单播)和基于文件。建议种子节点列表主要由集群中的那些master-eligible的节点组成。

9.1.3、Unicast(单播)

单播发现配置可用作种子节点的静态主机列表,这些主机可以指定为主机名或者IP地址;指定为主机名的主机在每轮Ping的时候会解析IP地址,这里需要注意下DNS变化的不确定性情况。


种子列表是通过discovery.zen.ping.unicast.hosts配置参数来进行设置的,多个主机之间通过逗号分隔。每个值都应该采用host:port或者host的方式。其中port默认的值为transport.profiles.default.port参数的值,如果没有设置该参数则会回退到transport.port参数对应的值。需要注意的是:必须将IpV6类型的主机括起来,例如:[:::1]。

另外discovery.zen.ping.unicast.resolve_timeout参数可以配置每轮Ping的时候等待DNS查找的时间。默认为5s。该模式下使用transport模块来实现发现的。

9.1.4、File-based(基于文件发现)

除了使用静态配置参数 discovery.zen.ping.unicast.hosts 设置提供的主机之外,还可以通过外部文件提供主机列表。es会在这个文件发生变化时重新加载它,这样种子节点列表就可以动态变化,而不需要重新启动每个节点。比如es运行在 Docker 容器中,当这些IP 地址在节点启动时可能不知道时,通过文件的方式动态提供一个 IP 地址列表以连接到 Zen 发现。如果要开启基于文件的发现,需要设置discovery.zen.hosts_provider:file。

然后创建$ES_PATH_CONF/unicast_hosts.txt中空文件,每当对该文件进行更改时,es都会重新加载来使用新的主机列表。

该文件的格式是每一行指定一个节点,由(主机名或者IP)和可选的端口,如果指定了端口号,一定要用:分隔紧跟在主机之后。如果未指定端口,则默认使用9300。

10.10.10.5
10.10.10.6:9305
10.10.10.5:10005
# an IPv6 address
[2001:0db8:85a3:0000:0000:8a2e:0370:7334]:9301

允许使用主机名代替 IP 地址(类似于 discovery.zen.ping.unicast.hosts),并且 IPv6 地址必须在括号中指定,括号后是端口。

也可以向该文件添加注释。所有注释都必须出现在以 # 开头的行中(即注释不能从一行的中间开始)。

需要注意的是:基于文件发现的插件增强了elasticsearch.yml中的Unicast模式,也就是说两者可以同时存在。除了discovery.zen.ping.unicast.hosts中的主机外,也会使用unicast_hosts.txt文件中的主机列表。

9.1.5、Master选举

在ping的过程当中,会进行集群master选举,这个动作是自动完成的。discovery.zen.ping_timeout(默认为 3s)决定了节点在决定开始选举或加入现有集群之前将等待多长时间。在此超时间隔内将发送三个 ping。如果超时后无法做出决定,则 ping 过程将重新启动。在网络拥堵的情况下是可以加大时间的,但是需要注意这有可能会减慢选举的过程。一旦节点加入到现有集群之

后,它就会向主节点发送加入请求,请求超时时间(discovery.zen.join_timeout)默认为ping超时的20倍。

当主节点出现故障的时候,集群会再次开始Ping并选举出一个新的主节点,当然这个Ping的作用还可以防止因为网络故障出现误判的情况。

如果discovery.zen.master_election.ignore_non_master_pings 参数(默认值为 false)设置为true,则会在master选举期间忽略来自不符合master 资格的节点(node.master为false 的节点)的ping。

discovery.zen.minimum_master_nodes(最小主节点个数)参数来控制需要加入当选主节点的最小个数,如果没有达到这个参数的配置,那么主节点就会下台,新的选举就会开始。可以理解成投票个数。该参数可以预防脑裂问题

9.1.6、容错

es中有两个故障检测进程会在后台长期运行。第一个是由主节点去ping 集群中的所有其他节点并验证它们是否存活。而在另一端,每个节点 ping 主节点以验证它是否还活着或需要启动选举过程。这里有几个参数控制:

setting

description

discovery.zen.fd.ping_interval

节点被ping的频率,默认为1s

discovery.zen.fd.ping_timeout

等待 ping 响应的时间,默认为 30s

discovery.zen.fd.ping_retries

ping重试次数,默认为3次

9.1.7、集群状态更新

主节点是集群中唯一可以更改集群状态的节点。主节点一次处理一个集群状态更新,应用所需的更改并将更新后的集群状态发布到集群中的所有其他节点。每个节点接收发布消息,确认它,但尚未应用它。如果 master 在一定时间内没有收到至少来自discovery.zen.minimum_master_nodes 节点的确认(由discovery.zen.commit_timeout 设置控制,默认为 30 秒,负值视为 0 秒),集群状态更改将被拒绝。

一旦有足够多的节点做出响应,集群状态就会被提交,并且会向所有节点发送一条消息。然后节点继续将新的集群状态应用到它们的内部状态。在继续处理队列中的下一个更新之前,主节点等待所有节点响应,直到超时。discovery.zen.publish_timeout 默认设置为 30 秒,从发布开始的那一刻开始计算。

展开阅读全文

页面更新:2024-05-08

标签:分词   副本   节点   字段   集群   索引   入门   类型   操作   文档   数据

1 2 3 4 5

上滑加载更多 ↓
推荐阅读:
友情链接:
更多:

本站资料均由网友自行发布提供,仅用于学习交流。如有版权问题,请与我联系,QQ:4156828  

© CopyRight 2020-2024 All Rights Reserved. Powered By 71396.com 闽ICP备11008920号-4
闽公网安备35020302034903号

Top