文档CRUD API分为单文档API和多文档API。这些API的索引名参数既可以是一个真正的索引的名称,也可以是某个索引的别名alias。
单文档API有:Index API、Get API、Delete API、Update API,多文档API有:Multi Get API、Bulk API、Delete By Query API、Update By Query API、Reindex API。
读和写文档
es中的每一个索引都被分成若干个分片,每一个分片可以有多个副本。这些副本称为副本组replication group,在添加文档或删除文档时必须保持同步。如果不这样做,则不同副本读取的结果可能不一样。使分片副本保持同步的过程称为数据复制模型(data replication model)。
es的数据复制模型基于主备模型。分片分为主分片和副本分片。主分片是操作索引的主入口,它负责校验索引操作并确保其正确性。一旦主分片接受了某个索引操作,它会将操作复制到其他副本。
基本写模型
es中的每一个索引操作首先会根据路由解析到特定的副本组,一般是根据文档ID。一旦副本组被确定,则操作会在内部被转发给该副本组的主分片。主分片负责校验操作并将其转发到其他副本。由于副本可以脱机,因此不需要转发给所有副本。es会维护一个应该接受该操作的副本列表。该列表称为同步副本集合(in-sync replica set),由主节点维护。顾名思义,这些是好分片副本的集合,保证已经处理了向用户发送了确认通知的所有索引操作。主分片会将所有操作复制到此集合中的每个副本。
主分片遵循如下基本流程:
1.校验传入的操作,并在结构无效是拒绝它(例如某个字段值类型错误)。
2.在本地执行操作,即索引或者删除相关文档。这也将验证字段的内容,并在需要时拒绝。
3.将操作转发给同步副本集合中的副本分片,如果有多个副本分片,则将并行转发。
4.一旦所有的副本分片都成功执行了操作并响应给主分片,则主分片向客户端发送请求执行成功确认通知。
故障处理
在索引过程中可能会出现许多问题-磁盘可能会损坏,节点可能会相互断开连接,或者某些错误的配置可能会导致副本分片操作失败,尽管在主分片上是操作成功的。这些是罕见的,但是主分片必须响应它们。
如果主分片挂掉了,那么持有主分片的这个节点将向主节点报告这个信息。索引操作将会等待最多1分钟(默认),直到主节点将某个副本分片提升为主分片。然后该操作会被转发给新的主分片处理。需要注意的是,主节点会监控各个节点的健康情况,并可能会主动降级主分片。当持有主分片的节点因为网络问题而与集群隔离时,通常会发生这种情况。
一旦在主分片上成功执行了索引操作,该主分片就必须处理在副本分片执行时的潜在故障。这可能是由于副本分片故障或者因为网络问题,而导致操作不能到达副本分片或者副本分片无法响应。所有这些都具有相同的结果:同步副本集合的一部分副本错过了即将被确认的操作。为了避免违反不变量,主分片将向主节点发送消息,请求从同步副本集合中删除有问题的副本分片。在主节点返回确认删除副本分片的通知后,主分片才会响应操作。需要注意的是,主节点也会指示另一个节点开始构建新的副本分片,以使系统还原到正常状态。
在将操作转发给副本分片时,主分片也会用副本分片来检验自己还是不是活跃的主分片。如果主分片因为网络分区而被隔离,则在它意识到自己已被降级之前还会处理传入的索引操作。副本分片会拒绝来自老的主分片的操作。当主分片收到副本分片的拒绝请求的响应后,它会联系主节点并将知道自己已被替换。之后会将操作路由到新的主分片。
基本读模型
es中的读取可以是根据ID非常轻量级的查找,也可以是具有复杂聚合的搜索请求,这些聚合会占用大量的cpu计算资源。主备模型的优点之一是它使所有副本分片保持一致,因此,同步副本集合中的单个副本分片就可提供读取请求。
当一个节点收到读取请求时,该节点负责将请求转发到保存相关分片的节点,整理响应并响应给客户端。我们将该节点称为该请求的协调节点。基本流程如下:
1.将读取请求解析到相关分片。需要注意的是,因为大多数搜索将被发送到一个或多个索引,因此通常需要从多个分片中读取,每一个分片代表数据的不同子集。
2.从分片副本组中选择每个相关分片的活跃副本分片,这可以是主分片,也可以是其他副本分片。默认情况下,es会在副本分片间轮询。
3.将分片级读取请求发送给所选分片。聚合结果并响应客户端。请注意如果是根据ID查找,则只有一个分片是相关的,聚合结果这一步会省略。
故障处理
当某分片无法响应读取请求时,协调节点将从该分片副本组中选择另一个副本,并将分片级读取请求发送到该副本。重复失败可能导致没有可用的副本分片。在某些情况下,比如_search,es更愿意快速响应,尽管只有部分结果,而不是等待问题得到解决。
Index API
index API 用于往索引中插入一个新的json格式的文档,使之可以搜索,或者替换一个文档。示例:
put twitter/_doc/1
{
"user" : "kimchy",
"post_date" : "2009-11-15T14:12:12",
"message" : "trying out Elasticsearch"
}
如果之前尚未创建索引,则索引操作会自动创建索引,并自动映射。如果之前创建了索引,但是没有建立映射,则索引操作会创建映射。
如果想禁用索引自动创建功能,则需要修改所有节点的配置文件,把action.auto_create_index设置为false。 如果想禁用自动映射功能,则同样需要修改所有节点的配置文件,把index.mapper.dynamic设置为false。
索引中的每个文档都有一个版本号。如果在命令中不指定版本号(version=?),则es在执行命令时不进行版本校验。反之,es会做版本校验。
在index API中指定version参数可以用于乐观锁并发控制。一个好的用例是事务的read-then-update,读出某文档的版本号后根据版本号修改该文档。在多线程情况下,最多只有一个线程能够修改成功。
put twitter/_doc/1?version=2
{
"message" : "elasticsearch now has versioning support!"
}
version=2表示要处理id=2且版本号为2的文档,处理完之后版本号会变成3。假如id=1的文档版本号不等于2,不管是小于2还是大于2,上面命令都会报错"version conflict, current version is different than the one provided"。
默认情况下,使用内部版本(internal)控制,版本号从1开始,且随更新而增加,包括删除。版本号还可以是外部值,要启用此功能,需要在命令上带上version_type=external,且version的值必须大于等于0。当使用外部值进行版本控制时,es会检查命令中版本号值是否大于当前该文档的版本号值,而不是是否等于。如果大于,则将索引文档并使用新版本号。如果不大于,则会报"version conflict, current version is higher or equal to the one provided"。注意,使用外部版本控制时,版本号值可以是0,而版本号值为0的文档不可以通过update_by_query API更新,也不能通过Delete_by_query API删除。
还可以在命令上带上op_type=create,指定只能创建,不能替换,相当于put if absend。当且仅当指定id的文档不存在时才会索引成功,但是假如已存在的话,会报错"version conflict, document already exists"。示例:
put twitter/_doc/2?op_type=create
{
"message" : "elasticsearch now has versioning support!"
}
索引文档时可以不指定文档id,es会自动创建一个id,这种情况下,op_type被自动设置为create。示例:
put twitter/_doc
{
"message" : "elasticsearch now has versioning support!"
}
默认情况下,es通过使用文档id的hash值来确定文档分配到哪个主分片上。可以使用routing参数,此时es会通过routing参数值的hash值来确定主分片。就好像redis集群可以通过设置多个key的前缀为相同的的{prefix},进而使得这些key存储在同一个redis节点上一样,我们在索引文档的时候可以设置相同的routing参数值来使得多个文档存储在同一个主分片上,也就是同一个es节点上。
Delete API
根据文档id删除文档。还可以同时指定版本号,以确保删除指定版本的文档,如果版本不匹配,就会报错"version conflict, current version is different than the one provided"。 The version number of a deleted document remains available for a short time after deletion to allow for control of concurrent operations. The length of time for which a deleted document’s version remains available is determined by the index.gc_deletes
index setting and defaults to 60 seconds.
如果在index的时候指定了routing值,则在删除的时候也必须指定相同的routing值,否则很可能会删不成功。示例:
delete /twitter/_doc/1?routing=kimchy
还可以设置超时时间(默认为1分钟)。示例:
delete /twitter/_doc/1?timeout=5m
Delete By Query API
把根据一定条件搜索出来的文档全部删除。
删除twitter索引中message字段值中包含some或者message(均不区分大小写)的全部文档:
post twitter/_doc/_delete_by_query
{
"query": {
"match": {
"message": "some message"
}
}
}
_delete_by_query会在命令开始的时候获取索引的快照,并使用内部版本控制删除它找到的内容。这意味着如果文档在索引快照后、es处理删除请求前发生更改,会出现版本冲突。在_delete_by_query执行期间,顺序执行多个搜索请求以查找所有要删除的文档。
Term Vectors 词向量
返回某文档字段值的分词信息。文档既可以是索引中已索引好的文档,也可以由人为提供。示例:
get /twitter/_doc/1/_termvectors
也可以只看某一个字段,示例:
get /twitter/_doc/1/_termvectors?fields=message
返回值中terms对应的json的key即是该文档字段值分词结果。
文档还可以不从索引中取,而是由人为提供,示例:
get /twitter/_doc/_termvectors
{
"doc" : {
"message" : "twitter test test test"
}
}
注意,假如twitter/_doc中没有message映射,则执行上述命令后term_vectors对应一个空json,看不到message字段值的分词情况。只有twitter/_doc中有message映射时,才能看到terms对应的json。
Multi termvectors API
用的时候再看官方文档学吧,感觉没多大用。
?refresh
Index、Update、Delete、Bulk APIs支持使用refresh参数来控制更改何时可见。refresh值可以是:
空字符串或者true:在操作发生后立即刷新相关的主分片和副本分片。
wait_for:Wait for the changes made by the request to be made visible by a refresh before replying。这不会立即刷新,而是等待刷新发生。默认情况下,es会每隔1s刷新一次,刷新间隔由index.refresh_interval指定,这个是动态属性,既可在创建索引时指定,也可在创建后更改。
false:默认值,不等待刷新就返回。
示例:
put twitter/_doc/5?refresh=true
{
"name":"heisenberg"
}