1. 基本概念
-
Index 索引
- Document 文档
- Type 类型
-
Node 节点
- Shard 分片
1.1 文档(Document)
1.1.1 文档
- Elasticsearch 是面向文档的,文档是所有可搜索数据的最小单位。
-
文档会被序列化成 JSON 格式,保存在 Elasticsearch 中。
- JSON 对象由字段构成。
- 每个字段都有对应的字段类型(字符串、数值、布尔、日期、二进制、范围)
-
每个文档都有一个 Unique ID
- 可以自己指定 ID
- 也可以由 Elasticsearch 自动生成。
1.1.2 JSON 文档
- 一篇文档类似于数据库表中的一条记录。
-
JSON 文档格式灵活,不需要预先定义格式。
- 字段的类型可以指定或者通过 Elasticsearch 自动推算。
- 支持数组、嵌套。
1.1.3 文档元数据
{
"_index" : "movies",
"_type" : "_doc",
"_id" : "8609",
"_score" : 1.0,
"_source" : {
"year" : 1923,
"title" : "Our Hospitality",
"@version" : "1",
"id" : "8609",
"genre" : ["Comedy"]
}
}
-
_index
:文档所属的索引名 -
_type
:文档所属的类型名 -
_id
:文档唯一 id -
_score
:文档相关性打分 -
_source
:文档的原始 JSON 数据 -
_version
:文档的版本信息
1.2 索引(Index)
1.2.1 索引
-
索引是文档的容器,是一类文档的集合。
-
Index
:体现了逻辑空间
的概念。每个索引都有自己的 Mapping 定义,用于定义包含的文档的字段名和字段类型。 -
Shard
:体现了物理空间
的概念,索引中的数据分布在Shard
上。
-
-
索引的
Mapping
和Setting
-
Mapping
:定义文档字段的类型。 -
Setting
:定义不同的数据分布。
-
1.2.2 索引的不同语义
- 名词:一个 Elasticsearch 集群中,可以创建很多个不同的索引。
-
动词:保存一个文档到 Elasticsearh 的过程也叫索引(indexing)
- Elasticsearch 创建倒排索引。
1.3 类型(Type)
-
7.0 之前
,一个 Index 可以设置多个 Types -
7.0 开始
,一个 Index 只能创建一个 Type:_doc
-
6.0 开始
,Type 被 Deprated。
-
1.4 REST API
-
GET /_cat/indices?v
:查看索引 -
GET /_cat/indices?v&health=green
:查看状态为绿的索引 -
GET /_cat/indices?v&s=docs.count:desc
:按照文档个数对索引进行排序
1.5 集群(Cluster)
1.5.1 分布式特性
-
高可用性
- 服务可用性:允许有节点停止服务
- 数据可用性:部分节点丢失,不会丢失数据。
-
可扩展性
- 请求量提升 / 数据的不断增长(将数据分布到所有节点上)
1.5.2 集群
- 不同的集群通过不同的名字来区分,默认为
elasticsearch
- 通过配置文件修改,或者在命令行指定
-E cluster.name=demo
进行设置 - 一个集群可以有一个或者多个节点。
1.6 节点(Node)
1.6.1 节点
-
节点是一个 Elasticsearch 实例
- 本质上是一个 java 进程。
- 一台机器上可以运行多个实例,但是生产环境建议一台机器只运行一个 Elasticsearch 实例。
- 每个节点都有名字,通过配置文件可以设置。或者启动实例的时候通过参数
-E node.name=node1
指定。 - 每一个节点启动之后,会分配一个 UID,保存在
data
目录下
1.6.2 Master-eligible Node 和 Master Node
-
每个节点启动后,默认就是一个 Master-eligible 节点。
- 可以设置
node.master:false
禁止
- 可以设置
- Master-eligible 可以参加选注流程,成为 Master 节点
- 当第一个节点启动时候,它会将自己选举成 Master 节点。
-
每个节点上都保存了集群的状态,只有 Master 节点才能修改集群的状态信息
- 任意节点都能修改信息会导致数据的不一致性。
-
集群状态,为了一个集群中必要的信息
- 所有的节点信息
- 所有的索及和其相关
Mapping
、Setting
信息 - 分片的路由信息
1.6.3 Data Node & Coordinating Node
-
Data Node
- 可以保存数据的节点,叫做 Data Node。
- 负责保存分片数据。
- 在数据扩展上起到重要作用。
-
Coordinating Node
- 负责接受 Client 的请求,将请求分发到合适的节点,最终把结果汇集到一起。
- 每个节点
默认
都起到了 Coordinating Node 的作用。
1.6.4 配置节点类型
- 开发环境,一个节点可以承担多种角色。
-
生产环境,应该设置单一角色的节点。
节点类型 配置参数 默认值 master eligible node.master true data node.data true ingest node.ingest true coordinating only / 每个节点默认都是 Coordinating Node machine learning node.ml true, 需要 enable x-pack
1.7 分片(Shard)
1.7.1 Primary Shard & Replica Shard
-
主分片(Primary Shard):
- 用以解决数据水平扩展的问题。通过主分片可以将数据分布到集群内的所有节点上。
- 一个主分片是一个运行的 Lucene 实例,是一个索引。
- 主分片数在索引创建时指定,后续不允许修改,除非 Reindex。
-
副本分片 (Replica Shard):
- 用以解决数据高可用的问题。
- 副本分片是主分片的副本。
- 副本分片数可以动态调整。
1.7.2 分片的设置
生产环境中分片的设置,需要提前做好容量规划。
-
分片数设置过小
- 导致无法增加节点实现水平扩展。
- 单个分片的数据量太大,导致数据重新分配耗时
-
分片数设置过大
- 7.0 开始,默认主分片设置成 1,解决了 over-sharding 的问题。
- 影响搜索结果的相关性打分,影响统计结果的准确性。
- 单个节点上过多的分片,导致资源浪费,同时也会影响性能。
1.7.3 查看集群的健康状况
GET /_cluster/health
{
"cluster_name" : "learn_es",
"status" : "green",
"timed_out" : false,
"number_of_nodes" : 3,
"number_of_data_nodes" : 3,
"active_primary_shards" : 7,
"active_shards" : 14,
"relocating_shards" : 0,
"initializing_shards" : 0,
"unassigned_shards" : 0,
"delayed_unassigned_shards" : 0,
"number_of_pending_tasks" : 0,
"number_of_in_flight_fetch" : 0,
"task_max_waiting_in_queue_millis" : 0,
"active_shards_percent_as_number" : 100.0
}
-
green
: 主分片与副本分片都正常分配。 -
yellow
: 主分片正常分配,有副本分片未能正常分配。 -
red
: 有主分片未能分配。
2. 文档的 CRUD
2.1 基本 API
2.1.1 说明
四种基本操作:
-
Index
:添加文档-
POST <index>/_doc
:添加的文档 id 为系统自动生成。 -
PUT <index>/_doc/<_id>
:如果该 id 的文档不存在则添加,存在则更新同时增加版本号(version
字段)。 -
POST <index>/_create/<_id>
:如果该 id 的文档已存在,则报错。 -
PUT <index>/_create/<_id>
:如果该 id 的文档已存在,则报错。
-
-
Get
:读取文档-
GET <index>/_doc/<_id>
:获取该 id 文档的元信息 -
GET <index>/_source/<_id>
:获取该 id 文档元信息中的_source
字段 -
HEAD <index>/_doc/<_id>
:判断该 id 文档是否存在,存在返回 200,不存在返回 404 -
HEAD <index>/_source/<_id>
:判断该 id 文档中的_source
字段是否存在,存在返回 200,不存在返回 404
-
-
Update
:更新文档-
POST <index>/_update/<_id>
:更新部分文档,body 体中使用doc
字段。
-
-
Delete
:删除文档-
DELETE /<index>/_doc/<_id>
:删除该 id 的文档,如果文档不存在 什么都不做
-
2.1.2 Index API
- 支持
自动生成
文档 id 和指定
文档 id。 -
自动生成文档 id。
- 语法:
POST <index>/_doc
-
demo:
POST users/_doc { "user" : "Mike", "phone" : "15512345678" } ----------- { "_index" : "users", "_type" : "_doc", "_id" : "RfXT_28B5V-KMglJX8bm", "_version" : 1, "result" : "created", "_shards" : { "total" : 2, "successful" : 2, "failed" : 0 }, "_seq_no" : 3, "_primary_term" : 1 }
- 语法:
-
指定文档 id
- 语法:
PUT <index>/_doc/<_id>
或者POST | PUT <index>/_create/<_id>
-
demo 1:
PUT <index>/_doc/<_id>
PUT users/_doc/1 { "user" : "John", "phone" : "15812345678" } -------- # 不存在该 id 的文档时,直接新增 { "_index" : "users", "_type" : "_doc", "_id" : "1", "_version" : 1, "result" : "created", "_shards" : { "total" : 2, "successful" : 2, "failed" : 0 }, "_seq_no" : 4, "_primary_term" : 1 } # 存在该 id 的文档时,替换文档(删除现有的,创建新的,version +1){ "_index" : "users", "_type" : "_doc", "_id" : "1", "_version" : 23, "result" : "updated", "_shards" : { "total" : 2, "successful" : 2, "failed" : 0 }, "_seq_no" : 26, "_primary_term" : 1 }
-
demo 2:
POST | PUT <index>/_create/<_id>
POST users/_create/2 { "user" : "Dave", "phone" : "15912345678" } --------- # 不存在该 id 的文档时,直接新增 { "_index" : "users", "_type" : "_doc", "_id" : "2", "_version" : 1, "result" : "created", "_shards" : { "total" : 2, "successful" : 2, "failed" : 0 }, "_seq_no" : 27, "_primary_term" : 1 } # 存在该 id 的文档时,version 冲突,报错。{ "error": { "root_cause": [ { "type": "version_conflict_engine_exception", "reason": "[2]: version conflict, document already exists (current version [1])", "index_uuid": "mjgjxIROT72xLMHnYNiUxw", "shard": "0", "index": "users" } ], "type": "version_conflict_engine_exception", "reason": "[2]: version conflict, document already exists (current version [1])", "index_uuid": "mjgjxIROT72xLMHnYNiUxw", "shard": "0", "index": "users" }, "status": 409 }
- 语法:
2.1.3 Get API
根据 id 查找文档
- 语法:
GET <index>/_doc/<_id>
-
demo:
GET users/_doc/2 -------- # 该 id 的文档存在,返回文档元信息 { "_index" : "users", "_type" : "_doc", "_id" : "2", "_version" : 1, "_seq_no" : 27, "_primary_term" : 1, "found" : true, "_source" : { "user" : "Dave", "phone" : "15912345678" } } # 该 id 的文档不存在,返回找不到 { "_index" : "users", "_type" : "_doc", "_id" : "2", "found" : false }
2.1.4 Update API
更新指定 id 的文档:
- 语法:
POST <index>/_update/<_id>
-
demo:更新部分文档
POST users/_update/1 { "doc": {"age":28} } -------- # 该 id 的文档存在,且字段值有变动 则更新文档;如果文档存在,且字段值无变动,result 为 noop { "_index" : "users", "_type" : "_doc", "_id" : "1", "_version" : 27, "result" : "updated", "_shards" : { "total" : 2, "successful" : 2, "failed" : 0 }, "_seq_no" : 34, "_primary_term" : 1 }
-
demo2:按照脚本更新文档
# index the doc PUT users/_doc/2 { "name" : "John", "counter" : 1 } { "_index" : "users", "_type" : "_doc", "_id" : "2", "_version" : 6, "result" : "updated", "_shards" : { "total" : 2, "successful" : 2, "failed" : 0 }, "_seq_no" : 53, "_primary_term" : 2 } -------- # update the doc POST users/_update/2 { "script": { "source": "ctx._source.counter += params.count", "params": {"count":2} } } { "_index" : "users", "_type" : "_doc", "_id" : "2", "_version" : 7, "result" : "updated", "_shards" : { "total" : 2, "successful" : 2, "failed" : 0 }, "_seq_no" : 54, "_primary_term" : 2 }
2.1.5 Delete API
根据 id 删除文档
- 语法:
Delete <index>/_doc/<_id>
-
demo:
DELETE users/_doc/2 -------- # 该 id 的文档存在,直接删除 { "_index" : "users", "_type" : "_doc", "_id" : "2", "_version" : 2, "result" : "deleted", "_shards" : { "total" : 2, "successful" : 2, "failed" : 0 }, "_seq_no" : 31, "_primary_term" : 1 } # 该 id 的文档不存在,什么都不做 { "_index" : "users", "_type" : "_doc", "_id" : "2", "_version" : 3, "result" : "not_found", "_shards" : { "total" : 2, "successful" : 2, "failed" : 0 }, "_seq_no" : 32, "_primary_term" : 1 }
2.2 其他 API
2.2.1 Bulk API
- 支持在一次 API 调用中,对不同的索引进行操作。
- 操作中单条操作失败,并不会影响其他操作。
- 返回结果包含了每一条操作的结果。
-
支持四种类型操作。
Index
Create
Update
Delete
-
语法;
POST _bulk
或者POST <index>/_bulk
`-
newline delimited JSON (NDJSON)结构
action_and_meta_data\n optional_source\n action_and_meta\_data\n optional_source\n .... action_and_meta_data\n optional_source\n
-
-
demo:
POST _bulk # index、create:下一行需要跟着 source {"index" : { "_index" : "test", "_id" : "1"} } {"field1" : "value1"} {"create" : { "_index" : "test", "_id" : "2"} } {"field2" : "value2"} # update 下一行需要跟着 doc 或者 script {"update" : {"_id" : "1", "_index" : "test"} } {"doc" : {"field3" : "value3"} } # delete 与标准 delete API 语法一样 {"delete" : { "_index" : "test", "_id" : "2"} }
2.2.2 mget API
- 批量读取文档
- 语法:
GET _mget
或者GET <index>/_mget
-
demo:
GET /_mget { "docs" : [ { "_index" : "users", "_id" : "1" }, { "_index" : "twitter", "_id" : "2" } ] } -------- { "docs" : [ { "_index" : "users", "_type" : "_doc", "_id" : "1", "_version" : 31, "_seq_no" : 38, "_primary_term" : 2, "found" : true, "_source" : { "user" : "abc", "class" : 8, "age" : 28, "gender" : "male", "field1" : "value1" } }, { "_index" : "twitter", "_type" : null, "_id" : "2", "error" : { "root_cause" : [ { "type" : "index_not_found_exception", "reason" : "no such index [twitter]", "resource.type" : "index_expression", "resource.id" : "twitter", "index_uuid" : "_na_", "index" : "twitter" } ], "type" : "index_not_found_exception", "reason" : "no such index [twitter]", "resource.type" : "index_expression", "resource.id" : "twitter", "index_uuid" : "_na_", "index" : "twitter" } } ] }
-
demo2:
GET users/_mget { "docs": [ {"_id" : "2"}, {"_id" : "3"} ] } GET users/_mget {"ids" : ["2", "3"] } -------- { "docs" : [ { "_index" : "users", "_type" : "_doc", "_id" : "2", "_version" : 7, "_seq_no" : 54, "_primary_term" : 2, "found" : true, "_source" : { "name" : "John", "counter" : 3 } }, { "_index" : "users", "_type" : "_doc", "_id" : "3", "found" : false } ] }
3. 倒排索引
3.1 组成
倒排索引包含两个部分:
-
单词词典(Term Dictionary):
- 记录所有文档的单词,记录单词到倒排列表的关联关系
- 一般比较大,通过 B + 树或者哈希拉链法实现,以满足高性能的插入与查询。
-
倒排列表(Posting List):
- 记录单词对应的文档结合,由倒排索引项组成。
-
倒排索引项:
- 文档 Id
- 词频 TF:该单词在文档中出现的次数,用于相关性评分。
- 位置(Position):单词在文档中分词的位置。用于语句搜索(phrase query)
- 偏移(Offset):记录单词的开始结束位置,实现高亮显示。
3.2 示例
在以下文档中搜索 Elasticsearch
-
文档内容
文档 Id 文档内容 1 Mastering Elasticsearch 2 Elasticsearch Server 3 Elasticsearch Essentials -
倒排列表
文档 Id 词频 TF 位置 偏移 1 1 1 <10,23> 2 1 0 <0,13> 3 1 0 <0,13>
3.3 Elasticsearch 的倒排索引
- Elasticsearch 的 JSON 文档的每个字段,都有自己的倒排索引。
-
可以指定对某些字段不做索引。
- 优点:节省存储空间
- 缺点:字段无法被搜索
4. 通过 Analyzer 进行分词
4.1 Analysis 与 Analyzer
-
Analysis:
- 文本分析,是把全文本转换为一系列单词 (term/ token) 的过程,也叫分词。
- 是通过 Analyzer 实现的,可以是内置分词器,也可以是定制分词器。
- 除了在数据写入时转换词条,匹配 Query 语句时也需要用相同的分析器对查询语句进行分析。
-
Analyzer:
- 分析器:一个分析器包括一个可选的字符过滤器、一个单个分词器、0 个或多个分词过滤器。
-
Analyzer 由三部分组成。
- Character Filters:字符过滤器,使用字符过滤器转变字符。
- Tokenizer:分词器,将文本切分为单个或者多个分词。
- Token Filter:分词过滤器,使用分词过滤器转变每个分词(小写、停用词、同义词)
4.2 Elasticsearch 内置分词器
- Standard Analayzer:
默认
分词器,按词切分,小写处理。 - Simple Analayzer:按照非字母切分(符号被过滤),小写处理。
- Stop Analayzer:小写处理,停用词过滤(the,a,is)
- Whitespace Analyzer:按照空格切分,不转小写。
- Keyword Analyzer:不分词,直接将输入当作输出。
- Pattern Analayzer:正则表达式,默认 W +(非字符分隔)
- Langugae:提供 30 多种常见语言的分词器。
- Customer Analyzer;自定义分词器
4.2.1 _analyze API
GET /_analyze
POST /_analyze
GET /<index>/_analyze
POST /<index>/_analyze
-
demo:
POST _analyze { "analyzer": "standard", "text": ["share your experience with NoSql & big data technologies"] }