# Bulk

与`mget`能同时允许帮助我们获取多个文档相同，`bulk` API可以帮助我们同时完成执行多个请求，比如：`create`，`index`, `update`以及`delete`。当你在处理类似于log等海量数据的时候，你就可以一下处理成百上千的请求，这个操作将会极大提高效率。

`bulk`的请求主体的格式稍微有些不同：

```javascript
{ action: { metadata }}\n
{ request body        }\n
{ action: { metadata }}\n
{ request body        }\n
...
```

这种格式就类似于一个用`"\n"`字符来连接的单行json一样。下面是两点注意事项：

* 每一行都结尾处都必须有换行字符`"\n"`，**最后一行也要有**。这些标记可以有效地分隔每行。
* 这些行里不能包含非转义字符，以免干扰数据的分析 — — 这也意味着JSON**不能**是pretty-printed样式。

> ## TIP

在《bulk格式》一章中，我们将解释为何`bulk` API要使用这种格式。

*action/metadata* 行指定了将要在**哪个文档**中执行**什么操作**。

其中*action*必须是`index`, `create`, `update`或者`delete`。*metadata* 需要指明需要被操作文档的`_index`, `_type`以及`_id`，例如删除命令就可以这样填写：

```javascript
{ "delete": { "_index": "website", "_type": "blog", "_id": "123" }}
```

在你进行`index`以及`create`操作时，*request body* 行必须要包含文档的`_source`数据——也就是文档的所有内容。

同样，在执行`update` API: `doc`, `upsert`,`script`的时候，也需要包含相关数据。而在删除的时候就不需要*request body*行。

```javascript
{ "create":  { "_index": "website", "_type": "blog", "_id": "123" }}
{ "title":    "My first blog post" }
```

如果没有指定`_id`，那么系统就会自动生成一个ID：

```javascript
{ "index": { "_index": "website", "_type": "blog" }}
{ "title":    "My second blog post" }
```

完成以上所有请求的`bulk`如下：

```javascript
POST /_bulk
{ "delete": { "_index": "website", "_type": "blog", "_id": "123" }} <1>
{ "create": { "_index": "website", "_type": "blog", "_id": "123" }}
{ "title":    "My first blog post" }
{ "index":  { "_index": "website", "_type": "blog" }}
{ "title":    "My second blog post" }
{ "update": { "_index": "website", "_type": "blog", "_id": "123", "_retry_on_conflict" : 3} }
{ "doc" : {"title" : "My updated blog post"} } <2>
```

1. 注意`delete`操作是如何处理*request body*的,你可以在它之后直接执行新的操作。
2. 请记住最后有换行符

Elasticsearch会返回含有`items`的列表、它的顺序和我们请求的顺序是相同的：

```javascript
{
   "took": 4,
   "errors": false, <1>
   "items": [
      {  "delete": {
            "_index":   "website",
            "_type":    "blog",
            "_id":      "123",
            "_version": 2,
            "status":   200,
            "found":    true
      }},
      {  "create": {
            "_index":   "website",
            "_type":    "blog",
            "_id":      "123",
            "_version": 3,
            "status":   201
      }},
      {  "create": {
            "_index":   "website",
            "_type":    "blog",
            "_id":      "EiwfApScQiiy7TIKFxRCTw",
            "_version": 1,
            "status":   201
      }},
      {  "update": {
            "_index":   "website",
            "_type":    "blog",
            "_id":      "123",
            "_version": 4,
            "status":   200
      }}
   ]
}}
```

1. 所有的请求都被成功执行。

每一个子请求都会被单独执行，所以一旦有一个子请求失败了，并不会影响到其他请求的成功执行。如果一旦出现失败的请求，`error`就会变为`true`，详细的错误信息也会出现在返回内容的下方：

```javascript
POST /_bulk
{ "create": { "_index": "website", "_type": "blog", "_id": "123" }}
{ "title":    "Cannot create - it already exists" }
{ "index":  { "_index": "website", "_type": "blog", "_id": "123" }}
{ "title":    "But we can update it" }
```

请求中的`create`操作失败，因为`123`已经存在，但是之后针对文档`123`的`index`操作依旧被成功执行：

```javascript
{
   "took": 3,
   "errors": true, <1>
   "items": [
      {  "create": {
            "_index":   "website",
            "_type":    "blog",
            "_id":      "123",
            "status":   409, <2>
            "error":    "DocumentAlreadyExistsException <3>
                        [[website][4] [blog][123]:
                        document already exists]"
      }},
      {  "index": {
            "_index":   "website",
            "_type":    "blog",
            "_id":      "123",
            "_version": 5,
            "status":   200 <4>
      }}
   ]
}
```

1. 至少有一个请求错误发生。
2. 这条请求的状态码为`409 CONFLICT`。
3. 错误信息解释了导致错误的原因。
4. 第二条请求的状态码为`200 OK`。

这也更好地解释了`bulk`请求是独立的，每一条的失败与否 都不会影响到其他的请求。

## 能省就省

或许你在批量导入大量的数据到相同的`index`以及`type`中。每次都去指定每个文档的metadata是完全没有必要的。在`mget` API中，`bulk`请求可以在URL中声明`/_index` 或者`/_index/_type`：

```javascript
POST /website/_bulk
{ "index": { "_type": "log" }}
{ "event": "User logged in" }
```

你依旧可以在metadata行中使用`_index`以及`_type`来重写数据，未声明的将会使用URL中的配置作为默认值：

```javascript
POST /website/log/_bulk
{ "index": {}}
{ "event": "User logged in" }
{ "index": { "_type": "blog" }}
{ "title": "Overriding the default type" }
```

## 最大有多大？

整个数据将会被处理它的节点载入内存中，所以如果请求量很大的话，留给其他请求的内存空间将会很少。`bulk`应该有一个最佳的限度。超过这个限制后，性能不但不会提升反而可能会造成宕机。

最佳的容量并不是一个确定的数值，它取决于你的硬件，你的文档大小以及复杂性，你的索引以及搜索的负载。幸运的是，这个*平衡点* 很容易确定：

试着去批量索引越来越多的文档。当性能开始下降的时候，就说明你的数据量太大了。一般比较好初始数量级是1000到5000个文档，或者你的文档很大，你就可以试着减小队列。 有的时候看看批量请求的物理大小是很有帮助的。1000个1KB的文档和1000个1MB的文档的差距将会是天差地别的。比较好的初始批量容量是5-15MB。


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://hezhiqiang.gitbook.io/elasticsearch/data/bulk.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
