ElasticSearch完全使用指南(一)

文章目录
  1. 1. 简介
  2. 2. 基本概念
    1. 2.1. 节点
    2. 2.2. 文档(Document)
    3. 2.3. 索引(Index)
    4. 2.4. 类型(Type)
    5. 2.5. 主分片和副本分片(Primary Shard & Replica Shard)
  3. 3. 数据类型
    1. 3.1. String类型
    2. 3.2. Number类型
    3. 3.3. Date类型
    4. 3.4. Boolean类型
  4. 4. 文档的CRUD操作
    1. 4.1. 文档创建(Create)
    2. 4.2. 索引文档(Index)
    3. 4.3. 查看文档(Read)
    4. 4.4. 文档更新(Update)
    5. 4.5. 删除文档(Delete)
    6. 4.6. 批量操作(Batch)
      1. 4.6.1. 批量取文档-mget
      2. 4.6.2. Bulk操作

ElasticSearch完全使用指南

简介

Elasticsearch是基于Apace Lunence构建的开源,分布式,具有高可用性和高拓展性的全文检索引擎。Elasticsearch具有开箱即用的特性,提供RESTful接口,是面向文档的数据库,文档存储格式为JSON,可以水平扩展至数以百计的服务器存储来实现处理PB级别的数据。

Elasticsearch可以快速存储,搜索,分析海量,索引数据速度达到毫秒级(近实时Near Real Time)。Github的代码搜索就是使用Elasticsearch来实现的。Elasticsearch使用场景有:

  1. 网站全文检索,高亮搜索,搜索建议
  2. 电商网站商品搜索,分面搜索(分面是指事物的多维度属性)
  3. 基于用户行为的推荐系统
  4. 支持到PB级别日志数据分析

基本概念

术语 说明
Node(节点) 节点是一个Elasticsearch实例。生产环境一般一台机器只运行一个Ealsticsearch
Cluster(集群) 由一个或多个节点组织在一起,共同工作,共同分享整个数据,具有负载均衡和故障转移功能的结构。集群中每个节点可以承担不同的角色
Index(索引) 是具有相似特征的文档集合,相当于关系型数据库的DB
Type(类型) 7.0之前一个索引中可以定义一种或多种类型,相当于关系型数据库的Table。7.0开始只能创建一个索引只能创建一个Type(_doc)
Document(文档) 文档是存储和检索的基本单元,相当于关系型数据库的Row,文档序列化为JSON格式存储
Field(字段) 文档里面的最小单位,是JSON对象的字段,相当于关系数据库的Column
Shards(分片) 索引分成若干份,每一份相当于shard,Shard体现了物理空间的概念
Replicas(副本) 索引的拷贝。用于负载均衡和保证数据读取的吞吐量和高可用
Mapping(映射) 每个索引都有自己的Mapping定义,用于定义包含的文档的字段字段名和字段类型,类似关系型数据字段类型

节点

一个节点就是一个Elasticsearch的实例,每个节点需要显示指定节点名称,可以通过配置文件配置,或者启动时候-E node.name=node1指定

节点类型

每个节点在集群承担承担不同的角色,也可以称为节点类型。

候选主节点(Master-eligible nodes)和主节点(Master Node)

  • 每个节点启动之后,默认就是一个Master eligible节点,Master-eligible节点可以参加选主流程,成为Master节点
  • 当第一个节点启动时候,它会将自己选举成为Master节点
  • 在每个节点上都保存了集群的状态信息,但只有Master节点才能修改集群的状态信息。集群状态(Cluster State)中必要信息包含
    • 所有节点的信息
    • 所有的索引,以及其Mapping与Setting信息
    • 分片的路由信息

数据节点(Data Node)和协调节点(Coordinating Node)和Ingest节点

  • Data Node
    • 用于保存数据的节点。负责保存分片的数据,在数据拓展上起到至关重要的作用
  • Coorination Node
    • 负责接受Client的请求,将请求分发到合适的节点,最终把结果汇集到一起
    • 每个节点默认都起到Cooridinating Node的职责,这就意味着如果一个node,将node.master,node.data,node.ingest全部设置为false,那么它就是一个纯粹的coordinating Node node,仅仅用于接收客户端的请求,同时进行请求的转发和合并
  • Ingest节点
    • 可以运行pipeline脚本,用来对document写入索引文件之前进行预处理的

在生产环境部署上可以部署dedicate的 Ingest Node 和 Coordinate node,在前端的Load Balance前面增加转发规则把读分发到coording node,写分发到 ingest node。 如果集群负载不高,可以配置一些节点同时具备coording和ingest的能力。然后将读写全部路由到这些节点。不仅配置简单,还节约硬件成本

其他类型节点

  • 冷热节点(Hot & Warm Node)

    • 不同硬件配置的Data Node,用来实现Hot & Warm架构,降低集群部署的成本。通过设置节点属性来实现
  • 机器学习节点(Machine Learning Node)

    • 负责跑机器学习的Job,用来异常检测

节点类型配置:

  • 开发环境一个节点可以承担多种角色
  • 生产环境中,应该设置单一的角色的节点,即dedicated node
节点类型 配置参数 默认值
候选主节点 node.master true
数据节点 node.data true
ingest节点 node.ingest true
协调节点 每个节点默认都是协调节点
机器学习节点 node.ml true

查看节点信息

我们可以通过以下API查看节点信息

1
GET /_cat/nodes?v

返回信息如下:

1
2
ip         heap.percent ram.percent cpu load_1m load_5m load_15m node.role master name
172.31.0.2 77 96 10 0.15 0.14 0.15 mdi * zy1AwuM

返回信息说明:

  • node.role值mdi是master,data,ingest首字母缩写,说明172.31.0.2节点承担着master,data,ingest角色。

  • master值是*(星号),说明该节点被选主成主节点

  • name值是zy1AwuM,说明节点名称是zy1AwuM

查看节点属性

通过下面的API,我们可以查看节点属性信息

1
GET /_cat/nodeattrs?v

文档(Document)

  • Elasticsearch是面向文档的Nosql,文档是可以搜索的数据的最小单位
  • 文档以序列化的Json格式存储,Json对象有字段组成,每个字段都有对应的字段类型。字段类型可以由Elasticsearch自动推断,或者手动指定(通过为索引创建Mapping)
  • 每个文档都有一个唯一ID。该ID可以由Elasticsearch自动生成,或者自己指定

索引(Index)

索引是是具有相似特征的文档集合,相当于关系型数据库的DB。每个索引都用自己的Mapping,用于定义索引包含的文档的Json对象的字段名称和类型。

索引可以分成若干份,可以存储在不同节点上。每一份索引相当于一个Shard。shard规则可以简单认为根据文档ID哈希处理之后,取主分片的模

1
shard = hash(document_id) % number_of_primary_shards

索引的主分片数是用过Settings来设置,它与Mapping(映射)区别是:

  • Mapping定义文档字段的类型
  • Setting定义主分片数,副本数

我们可以通过一下命令查看索引Index信息:

1
GET _cat/indices

类型(Type)

7.0 开始一个索引只能创建一个Type, 即_doc,在此之前一个索引可以设置多个Type

主分片和副本分片(Primary Shard & Replica Shard)

主分片

主分片用以解决数据水平拓展的问题。通过主分片,可以将数据分布到集群内的所有节点上。

  • 一个分片是一个运行的Luncene的实例
  • 主分片数在索引创建是指定,后续不允许修改,除非Reindex

副本

副本用以解决数据高可用的问题。副本是主分片的拷贝。当副本没有可用节点分配时候,集群状态是Yellow。

  • 副本分片数,可以动态调整
  • 增加副本数,在一定程度上提高读取的吞吐,提高服务的可用性

对于生成环境中分片的设定,需要提前做好容量规划,分片设置过程容易出现的问题:

  • 分片数设置过小
    • 后续无法通过增加节点实现水平拓展
    • 单个分片的数量过大,导致查询以及数据重新分配耗时过长
  • 分片数设置过大
    • 分片过大导致over-sharding问题
    • 单个节点上过大的分片,会导致资源浪费,也会影响性能
    • 影响搜索结果的相关性打分,影响统计结果的准确性。7.0开始默认主分片设置成1

数据类型

Elasticsearch数据类型

Elasticsearch中支持的数据类型非常丰富:

String类型

  • text:会被分词处理,用于全文检索,很少用于聚合处理(需要设置fielddata属性),不能用于排序
  • keyword:不可分词,用于精确搜索,过滤、排序、聚合等操作

Number类型

数字类型有如下分类:

类型 说明
byte 有符号的8位整数, 范围: [-128 ~ 127]
short 有符号的16位整数, 范围: [-32768 ~ 32767]
integer 有符号的32位整数, 范围: [−231 ~ 231-1]
long 有符号的64位整数, 范围: [−263 ~ 263-1]
float 32位单精度浮点数
double 64位双精度浮点数
half_float 16位半精度IEEE 754浮点类型
scaled_float 缩放类型的的浮点数, 比如price字段只需精确到分, 57.34缩放因子为100, 存储结果为5734

应当尽可能选择范围小的数据类型, 字段的长度越短, 索引和搜索的效率越高;优先考虑使用带缩放因子的浮点类型

Date类型

Date类型在Elasticsearch中以数值形式(long类型)存储。文档在索引Date类型数据时候,会根据format选项来指定日期格式,Elasticsearch默认解析ISO 8601格式字符串。format选项有:

  • 格式化的日期字符串

    比如yyyy-MM-dd格式的,2020-01-01,还有ISO8601格式的2020-01-01T05:04:03Z

  • 毫秒数

    比如1584930153000

  • 秒数

    比如1584930153

  • 多种格式混合

    多个格式使用双竖线||分隔,每个格式都会被依次尝试, 直到找到匹配的,比如yyyy-MM-dd HH:mm:ss||yyyy-MM-dd||epoch_millis

Boolean类型

  • 真值,表示真值的有true,“true”, “on”, “yes”, “1”
  • 假值,表示假值的有false, “false”, “off”, “no”, “0”, “”, 0.0, 0

文档的CRUD操作

不同于关系型数据库的写时模式(schema on write),Elasticsearch属于读时模式(schema on read)。在写入文档时候不需要明确设置各个字段类型,Elasticsearch会自动出推断字段类型。在真实项目中,我们应该明确通过设置索引的Mapping来指定字段和字段。

文档创建(Create)

1
2
3
4
5
POST /game/_doc
{
"name": "Temple Monkey Run 3D",
"size": "7.97M"
}

若文档创建成功,Elasticsearch会返回元数据和一个 201 Created的 HTTP状态码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
{
"_index" : "game",
"_type" : "_doc",
"_id" : "cks0JHEB43V9Cn92sBxT",
"_version" : 1,
"result" : "created",
"_shards" : {
"total" : 2,
"successful" : 1,
"failed" : 0
},
"_seq_no" : 0,
"_primary_term" : 1
}

以下划线(_)开头的字段属于Elasticsearch元信息(meta data).

  • _index

    索引名称,相当于mysql里面的数据库名称

  • _type

    索引类型,相当于mysql里面的表名

  • _id

    文档唯一ID。_index,_type,_id唯一标识了系统里面的一个文档。在未定文档ID时候,Elasticsearch会自动生成文档ID。自动生成的 ID 是基于 Base64编码、URL-safe、且长度为20个字符的 GUID 字符串。 这些 GUID 字符串由可修改的 FlakeID 模式生成,这种模式允许多个节点并行生成唯一ID ,且互相之间的冲突概率几乎为零。

  • _version

    版本号,Elasticsearch使用版本号来进行并发控制

  • result

    操作结果,创建成功时created

手动指定文档ID:

1
2
3
4
5
PUT /game/_doc/2?op_type=create
{
"name": "PUBG MOBILE LITE (Official)",
"size": "542.23M"
}

或者

1
2
3
4
5
PUT /game/_doc/2/_create
{
"name": "PUBG MOBILE LITE (Official)",
"size": "542.23M"
}

若文档已存在,Elasticsearch将会返回409 Conflict状态码,以及如下的错误信息:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
{
"error": {
"root_cause": [
{
"type": "version_conflict_engine_exception",
"reason": "[_doc][2]: version conflict, document already exists (current version [6])",
"index_uuid": "yUVNSMmXQhSgbdLspeS5ag",
"shard": "2",
"index": "game"
}
],
"type": "version_conflict_engine_exception",
"reason": "[_doc][2]: version conflict, document already exists (current version [6])",
"index_uuid": "yUVNSMmXQhSgbdLspeS5ag",
"shard": "2",
"index": "game"
},
"status": 409
}

索引文档(Index)

此时Index为动词,是将文档写入到elasticsearch里面。在文档不存在的情况下,与指定文档ID创建文档操作本质一样。在文档以存在情况下,会更新文档信息(在内部,Elasticsearch 已将旧文档标记为已删除,并增加一个全新的文档。 ,但它并不会立即消失。当继续索引更多的数据,Elasticsearch会在后台清理这些已删除文档, 此操作也叫Index操作),此时_version会加一,resultupdated

1
2
3
4
5
POST /game/_doc/2
{
"name": "PUBG MOBILE LITE (Official)",
"size": "542.23M"
}

查看文档(Read)

1
GET /game/_doc/2

文档存在时候响应内容如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
{
"_index" : "game",
"_type" : "_doc",
"_id" : "2",
"_version" : 5,
"_seq_no" : 4,
"_primary_term" : 1,
"found" : true,
"_source" : {
"name" : "PUBG MOBILE LITE (Official)",
"size" : "542.23M"
}
}
  • found

    值为true(找到指定文档), false(未找到指定文档)

  • _source

    source字段返回文档所有内容

若文档不存在,Elasticsearch会返回404 Not Found状态码, found值为false

文档更新(Update)

1
2
3
4
5
6
7
POST /game/_doc/2/_update
{
"doc" : {
"name" : "PUBG MOBILE LITE",
"views": 100
}
}

更改name字段值,新增一个views字段,此时候文档版本号_version会更新。

若要更新全部文档则需要使用文档索引操作

删除文档(Delete)

1
DELETE /game/_doc/2

若文档不存在,Elasticsearch会返回404 Not Found状态码, resultnot_found

1
2
3
4
5
6
7
8
9
10
11
12
13
14
{
"_index" : "game",
"_type" : "_doc",
"_id" : "4",
"_version" : 2,
"result" : "not_found",
"_shards" : {
"total" : 2,
"successful" : 1,
"failed" : 0
},
"_seq_no" : 24,
"_primary_term" : 1
}

即使文档不存在, _version 值仍然会增加。这是 Elasticsearch内部记录本的一部分,用来确保这些改变在跨多节点时以正确的顺序执行。

批量操作(Batch)

批量取文档-mget

文档在同一个索引内,可以把多个文档ID放入ids数组中批量查询

1
2
3
4
GET /game/_doc/_mget
{
"ids" : [ "cks0JHEB43V9Cn92sBxT", "2" ]
}

文档在多个索引中:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
GET /_mget
{
"docs" : [
{
"_index" : "game",
"_type" : "_doc",
"_id" : 1
},
{
"_index" : "game",
"_type" : "_doc",
"_id" : 2
}
]
}

Bulk操作

通过Bulk API,我们可以一次性进行多种类型操作:

  • Index
  • Create
  • Update
  • Delte

Bulk API 支持对不同的索引操作,单条操作成功或失败,不影响其他操作结果。返回结果包括了每一条操作的结果

Bulk API请求体格式如下:

1
2
3
4
{ action: { metadata }}\n
{ request body }\n
{ action: { metadata }}\n
{ request body }\n

action/metadata指定对哪个索引,哪个文档进行何种类型操作,操作类型只能是上面四种:

1
2
3
4
5
6
7
8
POST /_bulk
{ "delete": { "_index": "website", "_type": "blog", "_id": "123" }}
{ "create": { "_index": "website", "_type": "blog", "_id": "123" }}
{ "title": "My first blog post" }
{ "index": { "_index": "website", "_type": "blog" }}
{ "title": "My second blog post" }
{ "update": { "_index": "website", "_type": "blog", "_id": "123", "_retry_on_conflict" : 3} }
{ "doc" : {"title" : "My updated blog post"} }

索引或者类型都一样的时候,我们可以在URL指定索引和类型,那么请求体中就不需要在指定索引和类型。若请求体中指定了,则会覆盖掉URL中的索引和类型

1
2
3
POST /website/_bulk
{ "index": { "_type": "log" }}
{ "event": "User logged in" }