ElasticSearch入门

1. 简介

ElasticSearch是一个高可扩展的开源全文搜索分析引擎,可以用它近实时的来存储、搜索和分析大量的数据。
ElasticSearch的底层是开源库Lucene,但是,你没法直接用 Lucene,必须自己写代码去调用它的接口。Elastic 是 Lucene 的封装,提供了 REST API 的操作接口,开箱即用。

2. ElasticSearch中的基本概念

1. Cluster
集群,一个ES集群是由多个节点(Node)组成的,每个集群都有一个cluster name作为标识,在同一网段下的ElasticSearch实例会通过cluster name决定加入到那个集群下。集群是一个或者多个节点的集合,他们一起保存整个数据,并提供跨所有节点的联合索引和搜索功能。
问题:一个es服务可以有多个集群?

2. node
节点,一个ES实例就是一个node,一个机器可以有多个实例,所以并不是说一台机器就是一个node,大多数情况下,每个node运行在一个独立的环境或者虚拟机上。

3. index
索引,即一系列documents的集合

4. shard
(1). 分片,ES是分布式搜索引擎,每个索引有一个或多个分片,索引的数据被分配到各个分片上,相当于一桶水,用N个杯子装。
(2). 分片有助于横向扩展,N个分片会被尽可能平均地(rebalance)分配在不同的节点上(例如你有2个节点,4个主分片(不考虑备份),那么每个节点会分到2个分片,后来你增加了2个节点,那么你这4个节点上都会有1个分片,这个过程叫relocation,ES感知后自动完成) 问题:数据会尽可能平均分配到各个分片上吗?
(3). 分片是独立的,对于一个Search Request的行为,每个分片都会执行这个Request
(4). 每个分片都是一个Lucene Index,所以一个分片只能存放 Integer.MAX_VALUE - 128 = 2,147,483,519个docs。
5. replica
(1). 复制,可以理解为备份分片,相应地每个备份有对应的主分片(primary shard)
(2). 主分片和备分片不会出现在同一个节点上(防止单点故障),默认情况下一个索引创建5个分片一个备份(即5primary+5replica=10个分片)
(3). 如果你只有一个节点,那么5个replica都无法分配(unassigned),此时cluster status会变成Yellow
(4). 作用:对于分布式搜索引擎来说,分片及副本的分配将是高可用及快速响应的设计核心,主分片与副本分片都能处理查询请求,他们的唯一区别是只有主分片才能处理索引请求。此外副本策略提供了高可用和数据安全的保障,当所在的机器宕机,Elasticsearch使用其副本进行恢复,从而避免数据丢失。

6. 集群发现机制
比如当前我们启动了一个es进程,当启动了第二个进程时,这个进程作为一个node自动发现了集群,并加入了进去。
Shard负载均衡:比如现在有10个shard,集群中有三个节点,es会均衡的进行分配,以保证每个节点均衡的负载请求。
请求路由:当索引一个文档的时候,文档会被存储到一个主分片中。ElasticSearch通过下面这个公式决定索引到那个分片:
shard = hash(routing) % number_of_primary_shards
routing 是一个可变值,默认是文档的 _id ,也可以设置成一个自定义的值。 routing 通过 hash 函数生成一个数字,然后这个数字再除以 number_of_primary_shards (主分片的数量)后得到 余数 。这个分布在 0 到 number_of_primary_shards-1 之间的余数,就是我们所寻求的文档所在分片的位置。

7. Rebalance自动均衡
创建的索引默认有5个分片,只有一个节点时5个分片都在同一个node上,如果新加入一个node,那么ElasticSearch会重新把分片均衡到node上。

shard&replica机制梳理总结
1. index包含多个shard
2. 每个shard都是一个最小工作单元,承载部分数据,lucene实例,完整的建立索引和处理请求的能力
3. 增减节点时,shard会自动在nodes中负载均衡
4. primary shard和replica shard,每个document肯定只存在于某一个primary shard以及其对应的replica   shard中,不可能存在于多个primary shard
5. replica shard是primary shard的副本,负责容错,以及承担读请求负载
6. primary shard的数量在创建索引的时候就固定了,replica shard的数量可以随时修改
7. primary shard的默认数量是5,replica默认是1,默认有10个shard,5个primary shard,5个replica shard
8. primary shard不能和自己的replica shard放在同一个节点上(否则节点宕机,primary shard和副本都丢失,起不到容错的作用),但是可以和其他primary shard的replica shard放在同一个节点上,因此,这个index是由6个shard组成的
3. 查询数据
  1. 查询阶段包括以下三个步骤:
    在这里插入图片描述
客户端发送一个 search 请求到 Node 3 , Node 3 会创建一个大小为 from + size 的空优先队列。
Node 3 将查询请求转发到索引的每个主分片或副本分片中。每个分片在本地执行查询并添加结果到大小为 from + size 的本地有序优先队列中。
每个分片返回各自优先队列中所有文档的 ID 和排序值给协调节点,也就是 Node 3 ,它合并这些值到自己的优先队列中来产生一个全局排序后的结果列表。
当一个搜索请求被发送到某个节点时,这个节点就变成了协调节点。 这个节点的任务是广播查询请求到所有相关分片并将它们的响应整合成全局排序后的结果集合,这个结果集合会返回给客户端。

第一步是广播请求到索引中每一个节点的分片拷贝。就像 document GET requests 所描述的, 查询请求可以被某个主分片或某个副本分片处理, 这就是为什么更多的副本(当结合更多的硬件)能够增加搜索吞吐率。 协调节点将在之后的请求中轮询所有的分片拷贝来分摊负载。

每个分片在本地执行查询请求并且创建一个长度为 from + size 的优先队列—也就是说,每个分片创建的结果集足够大,均可以满足全局的搜索请求。 分片返回一个轻量级的结果列表到协调节点,它仅包含文档 ID 集合以及任何排序需要用到的值,例如 _score 。

协调节点将这些分片级的结果合并到自己的有序优先队列里,它代表了全局排序结果集合。至此查询过程结束。
  1. 获取结果阶段
    在这里插入图片描述

分布式阶段由以下步骤构成:

1. 协调节点辨别出哪些文档需要被取回并向相关的分片提交多个 GET 请求
2. 每个分片加载并丰富文档,如果有需要的话,接着返回文档给协调节点。
3. 一旦所有的文档都被取回了,协调节点返回结果给客户端。
协调节点首先决定哪些文档 确实 需要被取回。例如,如果我们的查询指定了 { "from": 90, "size": 10 } ,最初的90个结果会被丢弃,只有从第91个开始的10个结果需要被取回。这些文档可能来自和最初搜索请求有关的一个、多个甚至全部分片。
4. 协调节点给持有相关文档的每个分片创建一个 multi-get request ,并发送请求给同样处理查询阶段的分片副本。
5. 分片加载文档体-- _source 字段--如果有需要,用元数据和 search snippet highlighting 丰富结果文档。 
6. 一旦协调节点接收到所有的结果文档,它就组装这些结果为单个响应返回给客户端。

4. 副本策略

1. 副本原理
es为了更好地稳定性和容灾,可以对分片进行复制和数据同步形成副本,副本的添加可以更好地维持集群数据完整性。副本分片与主分片做着相同的工作。
在索引写入时,副本分片做着与主分片相同的工作。新文档首先被索引进主分片然后再同步到其它所有的副本分片。增加副本数并不会增加索引容量,但是可以通过副本的增加引入新的硬件能力,提升查询能力。

2. 副本作用

  1. 故障转移/集群恢复
    如果持有主分片的节点挂了,一个副本分片就会晋升为主分片 在索引写入时,副本分片做着与主分片相同的工作。新文档首先被索引进主分片然后再同步到其它所有的副本分片。
  2. 通过副本进行负载均衡
    搜索性能取决于最慢的节点的响应时间,所以尝试均衡所有节点的负载是一个好想法。 如果我们只是增加一个节点而不是两个,最终我们会有两个节点各持有一个分片,而另一个持有两个分片做着两倍的工作。我们可以通过调整副本数量来平衡这些。通过分配两份副本而不是一个,最终我们会拥有六个分片,刚好可以平均分给三个节点。
5. 使用Docker搭建ElasticSearch+Kibana

需要本地环境安装好docker,并且切换到阿里云的镜像源。
搭建步骤如下:

  1. 创建elasticsearch容器运行(8.2.0版本)
# 拉取elasticsearch镜像
docker pull elasticsearch:8.2.0
# 创建网络,为了连接kibana
docker network create esnetwork
# 创建容器并运行
docker run -d --name elasticsearch --net esnetwork -p 9200:9200 -p 9300:9300 -e "discovery.type=single-node" elasticsearch:8.2.0
  1. 创建kibana容器运行
    kibana是访问elasticsearch的客户端,虽然也可以用curl进行访问,但是kibana比较方便并且可以使用DQL。
# 拉取kibana镜像
docker pull kibana:8.2.0
# 创建容器并运行
docker run -d --name kibana --net esnetwork -p 5601:5601 kibana:8.2.0

访问http://localhost:5601,会提示输入token,
3. 获取kibana的token:

# 进入elasticsearch容器
docker exec -it elasticsearch /bin/bash
# 获取token,会打印在控制台
elasticsearch-create-enrollment-token --scope kibana

tips:退出上面的控制台输入exit回车即可。
4. 重新访问http://localhost:5601,会提示需要输入kibana验证码,获取code:

# 进入kibana容器
docker exec -it kibana /bin/bash
# 获取code
kibana-verification-code
  1. 输入code后提示输入账号密码,重设密码步骤:
# 进入elasticsearch容器(已在elasticsearch容器内可忽略)
docker exec -it elasticsearch /bin/bash
# 重新设置密码,执行后密码会打印出来
elasticsearch-reset-password -u elastic

然后使用账号名elastic和密码即可登陆kibana

3. 使用Kibana操作index

从登录的页面上打开左上侧的导航打开Management-Dev Tools,在这里插入图片描述

  1. Copy as cURL可以复制成curl的字符串
  2. Open documentation可以打开该命令的官方文档
  3. Auto indent可以格式化语句

1. 创建index

PUT /test
{
  "mappings": {
    "properties": {
      "id": {
        "type": "long"
      }
    }
  }
}

上面的语句创建了一个名字为test的index。

2. 给index添加字段

PUT /test/_mapping
{
  "properties": {
    "name": {
      "type": "text"
    }
  }
}

添加的字段如果没有设置值为默认为空,需要存储空间。插入的数据不需要跟索引中的映射字段一致。

3. 添加数据

POST /test/_doc
{
  "@timestamp": "2022-05-15T13:12:00",
  "id": 1,
  "name": "zxd"
}

DQL如下:

curl -XPOST "https://172.18.0.2:9200/test/_doc" -H 'Content-Type: application/json' -d'
{
  "@timestamp": "2022-05-15T13:12:00",
  "id": 1,
  "name": "zxd"
}'

DQL相比CURL就是需要用Kibana客户端调用,相当于是CURL的简写,省略了CURL的语句,省略了需要传递的header参数.

4. 查询数据

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

DQL如下:

curl -XGET "https://172.18.0.2:9200/test/_search" -H 'Content-Type: application/json' -d'
{
  "query": {
    "match_all": {}
  }
}'

5. 查询集群健康
curl: curl -XGET ‘localhost:9200/_cat/health?v&pretty’

DQL: GET /_cat/health?v&pretty

返回数据如下:

epoch      timestamp cluster        status node.total node.data shards pri relo init unassign pending_tasks max_task_wait_time active_shards_percent
1653909451 11:17:31  docker-cluster yellow          1         1     11  11    0    0        1             0                  -                 91.7%

无论何时我们请求集群健康,我们可以获得 green,yellow,或者 red 的 status。Green 表示一切正常(集群功能齐全), yellow 表示所有数据可用,但是有些副本尚未分配(集群功能齐全),red 意味着由于某些原因有些数据不可用。注意,集群是 red,它仍然具有部分功能(例如,它将继续从可用的分片中服务搜索请求),但是您可能需要尽快去修复它,因为已经丢失数据了。
从上面的响应中,我们可以看到共计 1 个 node(节点)(node.total)和 11 个 shard(分片)(shards).

6. 列出全部索引
curl -XGET 'localhost:9200/_cat/indices?v&pretty'
注:localhost:9200是elasticsearch的服务启动地址和端口。

响应如下:

health status index uuid                   pri rep docs.count docs.deleted store.size pri.store.size
yellow open   test  kUHYx63bTIWmQd3qwSdBFg   1   1          2            0      7.9kb          7.9kb

pri是主分片数量的意思,rep是副本数量的意思,docs.count是索引中的数据数量。
yellow意味着有些副本没有被分配。该索引发生这种情的原因是因为 Elasticsearch 默认为该索引创建了 1 个副本。因为目前我们只有一个节点在运行,这一个副本不能够被分配(为了高可用性),直到稍候其它节点加入到集群。如果副本被分配到第二个节点,该索引的 heath status(健康状态)将会转换成 green。

** 7. 查看节点状态**
DQL:

GET /_cat/nodes?v&pretty
8. 搜索
  1. 最简单的搜索,使用mathch_all来表示,例如搜索全部
GET /bank/_search
{
  "query": { "match_all": 
  {} 
  }
}

返回结果如下:

{
  "took" : 5,
  "timed_out" : false,
  "_shards" : {
    "total" : 1,
    "successful" : 1,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : {
      "value" : 2,
      "relation" : "eq"
    },
    "max_score" : 1.0,
    "hits" : [
      {
        "_index" : "test",
        "_id" : "cCzjE4EB4ZsY5LFU-WFK",
        "_score" : 1.0,
        "_source" : {
          "@timestamp" : "2022-05-15T13:12:00",
          "id" : 1,
          "name" : "zxd"
        }
      },
      {
        "_index" : "test",
        "_id" : "cSxqFIEB4ZsY5LFUC2Ec",
        "_score" : 1.0,
        "_source" : {
          "id" : 3,
          "name" : "cdl",
          "@timestamp" : "2022-05-15T13:12:00"
        }
      }
    ]
  }
}

返回字段含义:

  • took: ElasticSearch执行搜索的时间(毫秒)
  • time_out: 搜索是否超时
  • _shards:告诉我们多少个分片被搜索了,以及统计了成功/失败的搜索分片
  • hits:搜索结果
  • hits.total: 搜索结果的数量
  • hits.hits: 搜索结果的内容的数组
    数组中的内容:
    _index:所属索引
    _id:文档的id,不显示指定则由ElasticSearch分配一个String类型的id
    _score: 文档搜索关联分数(与查询语句的相关程度)
    _source:文档内容
  1. 查询数据以及排序
    下面的例子做了一个match_all并且返回了11到20的文档:
curl -XGET 'localhost:9200/bank/_search?pretty' -d'
{
  "query": { "match_all": {} },
  "from": 10,
  "size": 10
}'

注意:
如果在实际的搜索中没有指定size,默认为大小为10。如果没有指定from,默认为0。

from 参数(0 为基础)指定了文档开始的编号,size 参数指定了从 from 参数开始有多少个文档被返回。

  1. 匹配查询
    (1). 这个例子返回了账户编号为 20 的文档 :
curl -XGET 'localhost:9200/bank/_search?pretty' -d'
{
  "query": { "match": { "account_number": 20 } }
}'

(2). 这个例子返回了所有在 address 中包含 term 为 “mill” 的账户 :

curl -XGET 'localhost:9200/bank/_search?pretty' -d'
{
  "query": { "match": { "address": "mill" } }
}'

(3). 这个例子返回了所有在 address 中包含了 term 为 “mill” 或 “lane” 的账户:

curl -XGET 'localhost:9200/bank/_search?pretty' -d'
{
  "query": { "match": { "address": "mill lane" } }
}'

(4). 这个例子构建两个 match 查询,并且回了所有在 address 中包含了 “mill” 和 “lane” 的账户:

curl -XGET 'localhost:9200/bank/_search?pretty' -d'
{
  "query": {
    "bool": {
      "must": [
        { "match": { "address": "mill" } },
        { "match": { "address": "lane" } }
      ]
    }
  }
}'

(5). 这个例子构建两个 match 查询,并且回了所有在 address 中既不包含 “mill” 也不包含 “lane” 的账户 :

curl -XGET 'localhost:9200/bank/_search?pretty' -d'
{
  "query": {
    "bool": {
      "must_not": [
        { "match": { "address": "mill" } },
        { "match": { "address": "lane" } }
      ]
    }
  }
}'

(6). 这个例子返回了 age 为 40 但是 state 不为 ID 的账户 :

curl -XGET 'localhost:9200/bank/_search?pretty' -d'
{
  "query": {
    "bool": {
      "must": [
        { "match": { "age": "40" } }
      ],
      "must_not": [
        { "match": { "state": "ID" } }
      ]
    }
  }
}'

(7). 我们想要去找出余额大于或等于 20000 且小于或等于 30000 的账户:

curl -XGET 'localhost:9200/bank/_search?pretty' -d'
{
  "query": {
    "bool": {
      "must": { "match_all": {} },
      "filter": {
        "range": {
          "balance": {
            "gte": 20000,
            "lte": 30000
          }
        }
      }
    }
  }
}'

(8). 这个例子按 state 将所有的 account 给分组了,然后按 count 降序(默认)排序返回 top 10(默认)的 state :

curl -XGET 'localhost:9200/bank/_search?pretty' -d'
{
  "size": 0,
  "aggs": {
    "group_by_state": {
      "terms": {
        "field": "state.keyword"
      }
    }
  }
}'

在 SQL 中,上面的聚合概念类似下面 :
SELECT state, COUNT(*) FROM bank GROUP BY state ORDER BY COUNT(*) DESC

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值