ElasticSearch[2]-数据方面的基础概念
文章目录
文档(document)
大多数实体或对象(object)可以序列化位JSON格式的。在ES中,文档(document)特指最顶层的对象(root object)序列化成的JSON数据,并通过唯一标识存储在ES中。
文档元数据(metadata)
元数据(metadata)是关于文档的信息。
节点 | 意义 |
---|---|
_index | 文档存储的地方 |
_type | 文档代表的对象的类型 |
_id | 文档的唯一标识 |
- index(索引)
即索引,是存储和索引关联数据的地方。实际上数据真正存储在shards中,index只是一个把一个或多个shards分组在一起的逻辑空间。
索引的创建只需选择一个索引名称:不可以"_"开头,全部小写,不包含逗号。 - type(类型)
相同类型(type)的文档表示相同的“事物”。
每个类型(type)都有自己的映射(mapping)或者结构定义,所有类型下的文档被存储在同一个索引下,但是类型的mapping会告诉ES不同的文档如何被索引。
_type 的名字可以是大写或小写,不能包含下划线或逗号。 - _id
id仅仅是一个字符串,它与 _index 和 _type 组合时,就可以在Elasticsearch中唯一标识一个文档。当创建一个文档,可以自定义 _id ,也可以让ES自动生成。
索引文档
文档通过其 _index,_type,_id 唯一确定。
PUT & POST 创建/更新 文档
这里要提到一个机制,在 restful api风格中,put一般表示更新资源,post表示对创建资源。在ES中,put可以理解为:若指定的index、type、id可以找到一个已存在的数据,则对其进行更新操作,否则进行创建。
而post因为其id是由ES自动生成的所有始终是创建。
请求格式如下:
1 | PUT /{index}/{type}/{id} |
响应内容:
1 | { |
Elasticsearch中每个文档都有版本号,每当文档变化(包括删除,以及PUT的数据已存在)都会使 _version 增加。
GET 检索文档
请求格式:
1 | GET /{index}/{type}/{id}?pretty |
响应:
1 | { |
GET 检索文档的一部分
- 可以使用 _source 参数,值可以是多个字段使用逗号分隔 如下:
1 | GET /website/blog/123?_source=title,text |
- 只想得到 _source 字段而不要其他的元数据 如下:
1 | GET /website/blog/123/_source |
HEAD 检查文档是否存在
HEAD 请求不会返回响应体,只有HTTP头,可以通过 Status Code 判断是否存在资源:200存在/404不存在
重谈:关于 PUT 更新/创建 文档
前边已经说过,在ES的机制中 PUT 同时具有创建和更新两种操作,但是为了保证对数据的管理与请求的意愿是一致的。可以通过两种方式保证。
-
使用 op_type 查询参数
1
2PUT /{index}/{type}/{id}?op_type=create
{ ... } -
是在URL后加 /_create 做为端点
1
2PUT /{index}/{type}/{id}/_create
{ ... }此时,如果正常创建,ES会返回正常的元数据且响应状态码是 201 Created。如果包含相同的 _index 、 _type 和 _id 的文档已经存在,将返回 409 Conflict 响应状态码以及错误信息:
1
2
3
4
5{
"error" : "DocumentAlreadyExistsException[[website][4] [blog][123]:
document already exists]",
"status" : 409
}
DELETE 删除文档
请求格式:
1 | DELETE /{index}/{type}/{id} |
返回:
1 | 如果找到文档,返回状态 200 ok 响应体: |
尽管文档不存在 “found” 的值是 false 但是 _version 依旧会增加。这是内部记录的一部分,它确保在多节点间不同操作可以有正确的顺序。
关于并发操作的问题 本版控制
类似关系型数据库,在高并发的场景下,会出现幻读进而造成数据丢失的情况。
ES使用乐观锁的机制,使用 _version 字段保证所有修改都被正确排序。当一个旧版本出现在新版本之后,它会被简单的忽略。并且可以指定文档的 version 来做想要的更改。如果那个版本号不是现在的,请求就失败了。
也可以使用外部的本版控制系统,比如使用timestamp,可以在查询字符串后边添加 version_type=external 来使用这些版本号。外部版本号与之前说的内部版本号在处理的时候有些不同。它不再检查 _version 是否与请求中指定的一致,而是检查是否小于指定的版本。如果请求成功,外部版本号就会被存储到 _version 中。比如:
1 | 创建一个包含外部版本号5的新博客: |
局部更新
-
通过 /_update api 更新文档,请求表单接受一个局部文档参数 doc:
1
2
3
4
5
6
7POST /website/blog/1/_update
{
"doc" : {
"tags" : [ "testing" ],
"views": 0
}
} -
使用脚本局部更新,
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
371.
views 字段 +1:
POST /website/blog/1/_update
{
"script" : "ctx._source.views+=1"
}
2.
tags数组字段 添加新元素 search:
POST /website/blog/1/_update
{
"script" : "ctx._source.tags+=new_tag",
"params" : {
"new_tag" : "search"
}
}
3.
使用 upsert 参数定义文档来使其不存在时被创建
POST /website/pageviews/1/_update
{
"script" : "ctx._source.views+=1",
"upsert": {
"views": 1
}
}
4.
在不关心执行顺序时可以通过 retry_on_conflict 参数设置重试次数来自动完成 值默认为0
POST /website/pageviews/1/_update?retry_on_conflict=5
{
"script" : "ctx._source.views+=1",
"upsert": {
"views": 0
}
}
对于需要保证顺序的更新操作,则需要指定version信息
检索多个文档 mget
为了减少网络开销,ES支持合并请求检索多个文档。
1 | POST /_mget |
其返回结果的顺序与请求的数组顺序一致。
1 | { |
每个文档的检索和报告都是独立的,如果其中某些文档不存在不会影响到其他可以查询到的文档。
注:尽管前面提到有一个文档没有被找到,但HTTP请求状态码还是 200 。事实上,就算所有文档都找不到,请求也还是返回 200 ,原因是 mget 请求本身成功了。如果想知道每个文档是否都成功了,你需要检查 found 标志。
更新时的批量操作 bulk
bulk API允许我们使用单一请求来实现多个文档的 create\index\update\delete。
bulk 请求体如下:
1 | { action: { metadata }}\n |
用 “\n” 符号连接起来的一行一行的JSON文档流(stream),并且:
- 每行必须以 “\n” 符号结尾,包括最后一行。这些都是作为每行有效的分离而做的标记。
- 每一行的数据不能包含未被转义的换行符,它们会干扰分析,这意味着JSON不能被美化打印。
bulk 请求不是原子操作——它们不能实现事务。每个请求操作时分开的,所以每个请求的成功与否不干扰其它操作。