文档
- Elasticsearch 是 面向文档 的,意味着它存储整个对象或 文档,文档是ElasticSearch 可搜索数据的最小单位。
- 文档类似数据库的一条数据记录,有键值对
- 文档可以是一条日志,一个电影的描述信息等 - Elasticsearch 使用 JSON 作为文档的序列化格式,也就是说,文档数据会被 ElasticSearch 以 JSON 的方式存储。
- JSON 对象由字段以及属性组成
- JSON 对象的每个字段都有对应的字段类型(字符串 / 数值 / 布尔 / 日起 / 范围类型) - Elasticsearch 不仅存储文档,而且 索引 每个文档的内容,使之可以被检索。在 Elasticsearch 中,是对文档进行索引、检索、排序和过滤,而不是对行列数据,这也是 Elasticsearch 能支持复杂全文检索的原因。每个文档都有一个 Unique ID
- Id 可以是你自己指定的,必须唯一
- id也可以通过 ELasticSearch 自动生成
{
"_index" : "movies",
"_type" : "_doc",
"_id" : "1468",
"_score" : 1.0,
"_source" : {
"title" : "Booty Call",
"@version" : "1",
"id" : "1468",
"year" : 1997,
"genre" : [
"Comedy",
"Romance"
]
}
}
这就是 elastic的一个文档的格式,可以看到是按照JSON来存储的。
文档的元数据
文档是由一些元数据来描述的,元数据,用于描述的文档的一些基本信息,我们根据上边的这个文档结构来分析每个字段的元数据意义。
- _index - - 文档所属的索引名
- _type - - 文档所属的类型名,在7之前这个类型是可设置的,7之后这个类型默认都是_doc 并且不可以设置
- _id - - 文档的唯一Id,可以通过这个去查找文档
- _source - - 文档的原始数据,也就是我们存储的真正数据
- version - - 文档的版本,通过这个做mvvc控制,解决并发的版本冲突
- _score - - 相关性打分,可以用这个做排序
CRUD API
一下的所有Api 命令都是通过kibana的开发工具操作的,如果是 http 请求,那么将 es 的地址 拼在里边就可以
curl -XPOST "http://localhost:9210/users/_doc/1"
create 一个文档
- 请求格式是 请求方法 + 索引 + 文档类型(7默认为 _doc) + 数据,一般这个请求会去自动创建索引如果索引不存在的话,并且会自动去生成mapping,这个可以通过参数来控制不允许自动创建索引
- 通过 HTTP POST 创建文档并且自动生成 document ID
#create document. 自动生成 _id
POST users/_doc
{
"user" : "Mike",
"post_date" : "2019-04-15T14:12:12",
"message" : "trying out Kibana"
}
result:
{
"_index" : "users",
"_type" : "_doc",
"_id" : "cXg1JnQBubpWmoaWw7Pk",
"_version" : 1,
"result" : "created",
"_shards" : {
"total" : 2,
"successful" : 1,
"failed" : 0
},
"_seq_no" : 0,
"_primary_term" : 1
}
- 通过HTTP PUT 创建文档,需要制定当前操作是创建 _create , 必须指定 document ID,如果 ID 重复则报错
#create document. 指定 ID 如果已经存在,就报错
PUT users/_create/1
{
"user" : "Jack",
"post_date" : "2019-05-15T14:12:12",
"message" : "trying out Elasticsearch"
}
重复创建
{
"error": {
"root_cause": [
{
"type": "version_conflict_engine_exception",
"reason": "[1]: version conflict, document already exists (current version [1])",
"index_uuid": "pHCTrajVRj-TfgxVTJ9oiA",
"shard": "0",
"index": "users"
}
]
},
"status": 409
}
GET 获取一个文档
#Get the document by ID
GET users/_doc/1
result
{
"_index" : "users",
"_type" : "_doc",
"_id" : "1",
"_version" : 1,
"_seq_no" : 1,
"_primary_term" : 1,
"found" : true,
"_source" : {
"user" : "Jack",
"post_date" : "2019-05-15T14:12:12",
"message" : "trying out Elasticsearch"
}
}
index 一个文档
#Update 指定 ID (先删除,在写入)
PUT users/_doc/1
{
"user" : "Mike"
}
result
{
"_index" : "users",
"_type" : "_doc",
"_id" : "1",
"_version" : 2,
"result" : "updated",
"_shards" : {
"total" : 2,
"successful" : 1,
"failed" : 0
},
"_seq_no" : 2,
"_primary_term" : 1
}
update 一个文档
- 请求格式是 请求方法 + 索引 + 操作类型(_update) + 文档Id + 修改的数据
update 并不会与 index 一样先删除再新增,update是直接在原来的数据上进行更新操作,索引 如果是新增字段则可以考虑 使用 update Api,index在这种场景下不可用,因为他会把原先的数据给删除掉,所以之前的数据就会丢失
#在原文档上增加字段
POST users/_update/2/
{
"doc":{
"post_date" : "2019-05-15T14:12:12",
"message" : "trying out Elasticsearch"
}
}
delete 一个文档
# 删除文档
DELETE users/_doc/1
批量操作 BULK API
- 上边的请求都是一个个的请求,如果我们有批量的需求,那么会很费事,ElasticSearch 提供了批量的接口,也就是 BULK API ,支持在一次请求中 对不同的索引进行操作。
请求格式: POST + _bulk + 执行语句,如果是创建语句,那么在这条语句后边跟上你要修改的值
- 支持的操作类型 create index update delete
- 一次请求中单条语句失败并不会影响其他的语句执行
- 返回结果会将每条数据的执行结果返回
POST _bulk
{ "index" : { "_index" : "test", "_id" : "1" } }
{ "field1" : "value1" } # 这个是前边的index 索要执行的值
{ "delete" : { "_index" : "test", "_id" : "2" } } # delete 不需要修改值
{ "create" : { "_index" : "test2", "_id" : "3" } }
{ "field1" : "value3" }
{ "update" : {"_id" : "1", "_index" : "test"} }
{ "doc" : {"field2" : "value2"} }
批量获取 mget
#URI中指定index
GET /test/_mget
{
"docs" : [
{
"_id" : "1"
},
{
"_id" : "2"
}
]
}
索引
- index 索引是文档的容器,是一类文档的集合,功能类似于传统关系型数据库中的一个数据库。
- index 体现了逻辑空间的概念,是一个逻辑意义上的聚合。
- shard 分片体现了物理空间的观念,索引中的数据具体存放的地方。 - 索引在 ElasticSearch 中有名词与动词的区分,语义是不一样的。
- 名词,一个 索引 类似于传统关系数据库中的一个 数据库 ,是一个存储关系型文档的地方。
- 动词,索引一个文档 就是存储一个文档到一个 索引 (名词)中以便被检索和查询。这非常类似于 SQL 语句中的 INSERT 关键词。 - Mapping 与 Setting
- Mapping 定义文档字段的类型
- Setting 定义不同数据的分布,指定多少的分片,要怎么存储
{
"movies" : {
"aliases" : { },
"mappings" : {
"properties" : {
"@version" : {
"type" : "text",
"fields" : {
"keyword" : {
"type" : "keyword",
"ignore_above" : 256
}
}
},
"id" : {
"type" : "text",
"fields" : {
"keyword" : {
"type" : "keyword",
"ignore_above" : 256
}
}
}
}
},
"settings" : {
"index" : {
"creation_date" : "1597680584404",
"number_of_shards" : "1",
"number_of_replicas" : "1",
"uuid" : "28eYBLMIRPqf2t52acSobA",
"version" : {
"created" : "7030299"
},
"provided_name" : "movies"
}
}
}
}
这就是 ElasticSearch 的一个索引结构,可以看到是由两块的,一个是mappings,一个是 settings,mappings 中维护了每个字段的一些元数据信息,settings中更多的则是分片的一些信息,针对存储。
一些基本Api
# get 索引名 查看索引的元数据信息
GET movies
# 查看文档的统计信息
GET movies/_count
{
"count" : 9743,
"_shards" : {
"total" : 1,
"successful" : 1,
"skipped" : 0,
"failed" : 0
}
}
# 查看所有的索引
GET _cat/indices
yellow open movies 28eYBLMIRPqf2t52acSobA 1 1 9743 5 1.4mb 1.4mb
green open .kibana_task_manager xK3JGCoQTPiSjDWEBowOlg 1 0 2 0 31.7kb 31.7kb
green open kibana_sample_data_ecommerce 6jy0_oyhSmiqxLWlAnLqKw 1 0 4675 0 4.9mb 4.9mb
#查看索引并且模糊查找
GET /_cat/indices/kibana*?v&s=index
health status index uuid pri rep docs.count docs.deleted store.size pri.store.size
green open kibana_sample_data_ecommerce 6jy0_oyhSmiqxLWlAnLqKw 1 0 4675 0 4.9mb 4.9mb
green open kibana_sample_data_flights QIh5rLomSkqaaIVYSKzCKA 1 0 13059 0 6.5mb 6.5mb
#查看状态为绿的索引
GET /_cat/indices?v&health=green
health status index uuid pri rep docs.count docs.deleted store.size pri.store.size
green open .kibana_task_manager xK3JGCoQTPiSjDWEBowOlg 1 0 2 0 31.7kb 31.7kb
green open kibana_sample_data_ecommerce 6jy0_oyhSmiqxLWlAnLqKw 1 0 4675 0 4.9mb 4.9mb
#查看索引具体的字段并且状态为绿色的
GET /_cat/indices/kibana*?pri&v&h=health,index,pri,rep,docs.count,mt
health index pri rep docs.count mt pri.mt
green kibana_sample_data_ecommerce 1 0 4675 0 0
green kibana_sample_data_logs 1 0 14074 0 0
green kibana_sample_data_flights 1 0 13059 0 0
#查看索引使用了多少内存
GET /_cat/indices?v&h=i,tm&s=tm:desc
i tm
kibana_sample_data_ecommerce 203.4kb
kibana_sample_data_logs 81.1kb
kibana_sample_data_flights 72.7kb
movies 13.1kb
#清除索引
DELETE users
节点
- ElasticSearch 节点的概念,其实就是一个运行中的实例,每启动一个 ElasticSearch 就是一个节点
- 每个节点都有名字,可以通过配置文件配置,也可以在启动命令中添加参数 -E node.name=node1
- 每个节点启动都会生成一个 UID 放在data 目录下,所以启动时最好保证 data 目录唯一
节点的类型
Master-eligible node 和 master node
master 与 Master-eligible的概念,其实就是主从的概念,每个集群内部,一定是有一个Master的,然后集群内的其他节点为 Master-eligible 从节点,然后主节点挂掉的时候,从节点选举上位。
- 每个节点启动之后,默认就是一个 Master-eligible 节点
- Master-eligible 节点可以参与选举成为 master 节点,当集群中第一个节点启动时,他会将自己选举成为 master 节点
- 每个节点都保存了集群的状态,不过只有 master 节点才能修改集群的状态,master 节点负责管理集群范围内的所有变更,例如增加、删除索引,或者增加、删除节点等。
Data node 和 Coordinating node
- Data 节点顾名思义,就是保存数据的节点,负责保存分片数据。
- Coordinating node 有些类似网关,负责接收客户端的请求,并且将请求转发到合适的节点,最终再将结果聚合,每个节点都是一个 Coordinating node ,都可以负责路由转发
Hot node 和 Warm node
冷热节点,冷热节点的概念是根据机器的性能来区分的,比如 hot 热节点,如果机器的性能好的话,他可以作为 hot node,机器的性能差可以作为 warm node,有冷热节点的概念,其实就是为了节省费用,在日志场景下还是比较好用的,如果一些区域的日志吞吐量大可以使用 hot node,如果一些区域都是一系诶老的日志并且要慢慢处理掉的,可以使用 warm node。
配置节点类型
一个节点可以配置多个节点类型
node.master=true # 设置 Master-eligible,代表当前节点可以参与选举
node.data=true # 设置数据节点
node.ingest # 设置
node.ml=true # 设置这个节点为异常节点
分片
- 主分片
一个主分片其实就是一个 lucene 实力,主分片主要是用来解决水平扩容的问题,通过主分片,可以将数据分散到所有的节点上,文档这些数据就存放在分片中。
- 主分片在创建索引时就已经指定好了,不允许修改(真要修改也可以,Reindex Api) - 副本分片
是朱分片的拷贝,用于解决高可用的问题,并且副分片一般不与主分片在一个node上,这样这个节点挂掉,副分片依然存在,数据不会丢失
副本分片的数量是可以设置的
路由文档到分片
当索引一个文档的时候,文档会被存储到一个主分片中。 Elasticsearch 如何知道一个文档应该存放到哪个分片中呢?当我们创建文档时,它如何决定这个文档应当被存储在分片 1 还是分片 2 中呢?
首先这肯定不会是随机的,否则将来要获取文档的时候我们就不知道从何处寻找了。实际上,这个过程是根据下面这个公式决定的:
shard = hash(routing) % number_of_primary_shards
routing 是一个可变值,默认是文档的 _id ,也可以设置成一个自定义的值。 routing 通过 hash 函数生成一个数字,然后这个数字再除以 number_of_primary_shards (主分片的数量)后得到 余数 。这个分布在 0 到 number_of_primary_shards-1 之间的余数,就是我们所寻求的文档所在分片的位置。
分片的集群中部署
主副本分片的出现是为了保证数据的高可用的,那么这些分片在 ElasticSearch 中时怎样分布的才能保证数据的高可用。 我们 假设有一个集群由三个节点组成。 有两个主分片 P0 P1,每个主分片有两个副本分 片R0 R1。在分配副本分片的时候,相同分片的副本不会放在同一节点,所以有些类似下图,这样当 node 1 挂掉的时候, node2 以及 node3 中都有R0 的数据,保证高可用。
与关系型数据库的对比
ElasticSearch 在 7 之后默认已经不支持 type的创建,都是 _doc 类型
mysql | ElasticSearch |
database 数据库 | index 索引 |
table 数据表 | type 文档类型 |
data 数据 | document 文档 |
schemal DDL表格元数据信息 | mapping 索引字段元数据描述信息 |
Column 列 | field 字段 |