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
: 添加的文档id为系统自动生成。/_doc -
PUT
:如果该id的文档不存在则添加,存在则更新同时增加版本号(/_doc/<_id> version
字段)。 -
POST
:如果该id的文档已存在,则报错。/_create/<_id> -
PUT
:如果该id的文档已存在,则报错。/_create/<_id>
-
-
Get
:读取文档-
GET
:获取该id文档的元信息/_doc/<_id> -
GET
:获取该id文档元信息中的/_source/<_id> _source
字段 -
HEAD
:判断该id文档是否存在,存在返回200,不存在返回404/_doc/<_id> -
HEAD
:判断该id文档中的/_source/<_id> _source
字段是否存在,存在返回200,不存在返回404
-
-
Update
:更新文档-
POST
:更新部分文档,body体中使用/_update/<_id> doc
字段。
-
-
Delete
:删除文档-
DELETE /
:删除该id的文档,如果文档不存在 什么都不做/_doc/<_id>
-
2.1.2 Index API
- 支持
自动生成
文档id和指定
文档id。 -
自动生成文档id。
- 语法:
POST
/_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
或者/_doc/<_id> POST | PUT
/_create/<_id> -
demo 1:
PUT
/_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
/_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
/_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
/_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
/_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
`/_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
/_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 /
/_analyze POST /
/_analyze -
demo:
POST _analyze { "analyzer": "standard", "text": ["share your experience with NoSql & big data technologies"] }