[TOC]
一个文档在es中的角色相当于在数据库中的一行数据,区别在于文档相对于数据库记录可以是非结构化的数据,且文档是独立的,相互之间无法在存储层添加类似外键的关联关系。
文档读写模型
Es的索引会划分成shard(分片),且每个分片可以有多个副本.当添加文档和删除文档时,需要在主分片和副本分片之间进行同步处理,以保证各个副本和主分片的数据一致.
Es的数据冗余模型是基于主从模型的,主分片作为所有索引操作的主入口点,负责验证请求和执行请求以及将请求广播到副本分片中.
写
Every indexing operation in Elasticsearch is first resolved to a replication group using routing, typically based on the document ID. 确定分片之后,首先在主分片中执行操作,然后推进到其他副本分片中. Instead, Elasticsearch maintains a list of shard copies that should receive the operation. This list is called the in-sync copies and is maintained by the master node.
主分片遵循以下基本流程:
- 验证到达的操作,如果结构上非法就拒绝;
- 在本地执行操作,这一步会验证字段的内容,如果非法也会拒绝执行;
- 推进操作到同步副本集合中,如果有多个副本,就并行去执行;
- 当所有副本上的操作执行结束且返回给主分片,主分片向客户端确认请求完成.
以上是未发生异常的情况.
可能的问题:
- 主分片故障
当主分片自身故障时,宿主节点会想master节点发送消息。索引操作会等待master节点从冗余分片组中选举一个新的主分片来接收和处理请求直到超时。新的主分片选出来后,操作会转移到新的主分片,主分片处理请求,复制请求到副本中。
- 主分片在接收到请求后被降级
当主分片转发操作到副本时,会通过副本分片来确认自身是否仍然是主分片。来自旧主分片的请求会被副本分片拒绝,然后主分片会意识到自身已经不是主分片。操作会路由到新的主分片上。
- 副本分片故障
主分片会通知master节点移除故障分片,并在之后重新生成替代的副本节点。
- 无副本分片
只有主分片的索引也是合法的,只是没有数据冗余,一旦单点故障,数据可能丢失。
读(search)
当一个节点(coordinating node)收到读请求时,此节点负责转发请求到存储相关分片的节点,以及组装响应返回给client。此节点称为coordinating node。
基本流程如下:
- 解析读请求到相关的分片上;
- 选择每个相关分片的active副本,这个副本可以是主分片也可以是普通副本分片。 By default, Elasticsearch will simply round robin between the shard copies.
- 发送分片级别的读请求到选定的副本;
- 组合结果和响应。
问题处理:
当一个shard响应失败时,coordinating node会从同一个副本分片组中选择一个新的分片替代。重复的失败会导致没有分片副本可用。在某些情况下,比如_search,ES会先使用部分结果进行响应,而不会等待所有的结果返回。
文档API
文档和索引是紧密相关的,文档只有被索引了才能被检索,否则一个独立的文档没有意义。文档和索引的关系类似于数据和数据库表的关系。
文档是存储到索引中的,在存储文档之前,需要先创建索引(名词,存储的位置),当文档存储到索引中后,会对文档执行扫描来建立索引(索引操作),使其能够被检索。另外文档还有一个属性为type,索引的默认type是_doc,再添加文档时可以自行定义,类似于数据库的表名。在ES6.0之前,一个索引可以拥有多个type,但是在6.0之后一个索引只能有一个type。
在创建和配置好索引之后,创建文档即导入数据到索引中。ES会存储文档并对文档扫描创建索引,使其可以检索。
Get
- 取得一个文档
文档的唯一标识是由索引、类型和文档ID来确定的,所以要获取一个文档,需要提供这三个内容,使用以下的形式:
GET <index>/<type>/<ID>
- 判断文档是否存在
使用前述的GET形式也可以,如果不存在则返回类似:
{
"_index": "user",
"_type": "_doc",
"_id": "1",
"found": false
}
另外一种方式是使用HEAD请求:HEAD user/_doc/1
,如果不存在返回404 - Not Found
,存在则返回200 - OK
。
- 示例
# 指定路由策略、指定需要的字段
GET user/doc/_9a7aWQBnKU3XbyaDBsY?routing=_id&_source=score,first_name,last_name
# 不需要字段
GET user/doc/_9a7aWQBnKU3XbyaDBsY?routing=_id&_source=false
# 指定包含部分字段
GET user/doc/_9a7aWQBnKU3XbyaDBsY?routing=_id&_source_includes=id,first_name
# 指定排除部分字段
GET user/doc/_9a7aWQBnKU3XbyaDBsY?routing=_id&_source_excludes=id
Multi Get
GET /_mget
{
"docs" : [
{
"_index" : "user",
"_type" : "doc",
"_id" : "1"
},
{
"_index" : "user",
"_type" : "doc",
"_id" : "2"
}
]
}
# 过滤_source
GET /user/doc/_mget
{
"docs":[
{
"_id":"Bda7aWQBnKU3XbyaDBwY",
"_source":"false"
},
{
"_id":"ANa7aWQBnKU3XbyaDBwY",
"_source" : ["last_name", "first_name"]
},
{
"_id":"Cda7aWQBnKU3XbyaDBwY",
"_source" :{
"include":"id",
"exclude":"last_name"
}
}
]
}
Delete
- 指定删除
DELETE <index>/<type>/<ID>
- 匹配删除
POST user/_delete_by_query
{
"query":{
"match":{
"_id": "_9a7aWQBnKU3XbyaDBsY"
}
}
}
# 冲突时继续执行
POST twitter/_doc/_delete_by_query?conflicts=proceed
{
"query": {
"match_all": {}
}
}
# 批量删除的大小
POST twitter/_delete_by_query?scroll_size=5000
{
"query": {
"term": {
"user": "kimchy"
}
}
}
_delete_by_query启动时获取索引的快照,并使用内部版本控制删除它发现的内容。这意味着,如果在捕获快照的时间和处理删除请求的时间之间更改文档,就会出现版本冲突。当版本匹配时,将删除文档。
Update
更新一个文档可以直接像添加文档一样(但是必须要指定ID)PUT一个文档,来覆盖已有的文档,如:
# 添加
PUT user/doc/1
{
"id":"1",
"first_name":"test",
"last_name":"last",
"gender":"0"
}
# 查看
GET user/doc/1
# 更新
PUT user/doc/1
{
"id":"1",
"first_name":"first",
"last_name":"last",
"gender":"0"
}
- 脚本方式更新
POST test/_doc/1/_update
{
"script" : {
"source": "ctx._source.counter += params.count",
"lang": "painless",
"params" : {
"count" : 4
}
}
}
- 部分更新
POST user/doc/1/_update
{
"doc":{
"name":"newname"
}
}
- 删除字段
POST user/doc/1/_update
{
"script": "ctx._source.remove('name')"
}
- 匹配更新——_update_by_query
POST twitter/_update_by_query
{
"script": {
"source": "ctx._source.likes++",
"lang": "painless"
},
"query": {
"term": {
"user": "kimchy"
}
}
}
Bulk API
POST _bulk
{ "index" : { "_index" : "test", "_type" : "_doc", "_id" : "1" } }
{ "field1" : "value1" }
{ "delete" : { "_index" : "test", "_type" : "_doc", "_id" : "2" } }
{ "create" : { "_index" : "test", "_type" : "_doc", "_id" : "3" } }
{ "field1" : "value3" }
{ "update" : {"_id" : "1", "_type" : "_doc", "_index" : "test"} }
{ "doc" : {"field2" : "value2"} }
最后一行要以换行符结尾, When sending requests to this endpoint the Content-Type header should be set to application/x-ndjson。
网友评论