## 更新時的批量操作
就像`mget`允許我們一次性檢索多個文檔一樣,`bulk` API允許我們使用單一請求來實現多個文檔的`create`、`index`、`update`或`delete`。這對索引類似于日志活動這樣的數據流非常有用,它們可以以成百上千的數據為一個批次按序進行索引。
`bulk`請求體如下,它有一點不同尋常:
```Javascript
{ action: { metadata }}\n
{ request body }\n
{ action: { metadata }}\n
{ request body }\n
...
```
這種格式類似于用`"\n"`符號連接起來的一行一行的JSON文檔**流(stream)**。兩個重要的點需要注意:
* 每行必須以`"\n"`符號結尾,**包括最后一行**。這些都是作為每行有效的分離而做的標記。
* 每一行的數據不能包含未被轉義的換行符,它們會干擾分析——這意味著JSON不能被美化打印。
> 提示:
> 在《批量格式》一章我們介紹了為什么`bulk` API使用這種格式。
**action/metadata**這一行定義了**文檔行為(what action)**發生在**哪個文檔(which document)**之上。
**行為(action)**必須是以下幾種:
| 行為 | 解釋 |
| -------- | ------------------------------------------------------ |
| `create` | 當文檔不存在時創建之。詳見《創建文檔》 |
| `index` | 創建新文檔或替換已有文檔。見《索引文檔》和《更新文檔》 |
| `update` | 局部更新文檔。見《局部更新》 |
| `delete` | 刪除一個文檔。見《刪除文檔》 |
在索引、創建、更新或刪除時必須指定文檔的`_index`、`_type`、`_id`這些**元數據(metadata)**。
例如刪除請求看起來像這樣:
```Javascript
{ "delete": { "_index": "website", "_type": "blog", "_id": "123" }}
```
**請求體(request body)**由文檔的`_source`組成——文檔所包含的一些字段以及其值。它被`index`和`create`操作所必須,這是有道理的:你必須提供文檔用來索引。
這些還被`update`操作所必需,而且請求體的組成應該與`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`**行為(action)**沒有請求體,它緊接著另一個**行為(action)**
- <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> 這個請求的HTTP狀態碼被報告為`409 CONFLICT`。
- <3> 錯誤消息說明了什么請求錯誤。
- <4> 第二個請求成功了,狀態碼是`200 OK`。
這些說明`bulk`請求不是原子操作——它們不能實現事務。每個請求操作時分開的,所以每個請求的成功與否不干擾其它操作。
## 不要重復
你可能在同一個`index`下的同一個`type`里批量索引日志數據。為每個文檔指定相同的元數據是多余的。就像`mget` API,`bulk`請求也可以在URL中使用`/_index`或`/_index/_type`:
```Javascript
POST /website/_bulk
{ "index": { "_type": "log" }}
{ "event": "User logged in" }
```
你依舊可以覆蓋元數據行的`_index`和`_type`,在沒有覆蓋時它會使用URL中的值作為默認值:
```Javascript
POST /website/log/_bulk
{ "index": {}}
{ "event": "User logged in" }
{ "index": { "_type": "blog" }}
{ "title": "Overriding the default type" }
```
## 多大才算太大?
整個批量請求需要被加載到接受我們請求節點的內存里,所以請求越大,給其它請求可用的內存就越小。有一個最佳的`bulk`請求大小。超過這個大小,性能不再提升而且可能降低。
最佳大小,當然并不是一個固定的數字。它完全取決于你的硬件、你文檔的大小和復雜度以及索引和搜索的負載。幸運的是,這個**最佳點(sweetspot)**還是容易找到的:
試著批量索引標準的文檔,隨著大小的增長,當性能開始降低,說明你每個批次的大小太大了。開始的數量可以在1000~5000個文檔之間,如果你的文檔非常大,可以使用較小的批次。
通常著眼于你請求批次的物理大小是非常有用的。一千個1kB的文檔和一千個1MB的文檔大不相同。一個好的批次最好保持在5-15MB大小間。
- Introduction
- 入門
- 是什么
- 安裝
- API
- 文檔
- 索引
- 搜索
- 聚合
- 小結
- 分布式
- 結語
- 分布式集群
- 空集群
- 集群健康
- 添加索引
- 故障轉移
- 橫向擴展
- 更多擴展
- 應對故障
- 數據
- 文檔
- 索引
- 獲取
- 存在
- 更新
- 創建
- 刪除
- 版本控制
- 局部更新
- Mget
- 批量
- 結語
- 分布式增刪改查
- 路由
- 分片交互
- 新建、索引和刪除
- 檢索
- 局部更新
- 批量請求
- 批量格式
- 搜索
- 空搜索
- 多索引和多類型
- 分頁
- 查詢字符串
- 映射和分析
- 數據類型差異
- 確切值對決全文
- 倒排索引
- 分析
- 映射
- 復合類型
- 結構化查詢
- 請求體查詢
- 結構化查詢
- 查詢與過濾
- 重要的查詢子句
- 過濾查詢
- 驗證查詢
- 結語
- 排序
- 排序
- 字符串排序
- 相關性
- 字段數據
- 分布式搜索
- 查詢階段
- 取回階段
- 搜索選項
- 掃描和滾屏
- 索引管理
- 創建刪除
- 設置
- 配置分析器
- 自定義分析器
- 映射
- 根對象
- 元數據中的source字段
- 元數據中的all字段
- 元數據中的ID字段
- 動態映射
- 自定義動態映射
- 默認映射
- 重建索引
- 別名
- 深入分片
- 使文本可以被搜索
- 動態索引
- 近實時搜索
- 持久化變更
- 合并段
- 結構化搜索
- 查詢準確值
- 組合過濾
- 查詢多個準確值
- 包含,而不是相等
- 范圍
- 處理 Null 值
- 緩存
- 過濾順序
- 全文搜索
- 匹配查詢
- 多詞查詢
- 組合查詢
- 布爾匹配
- 增加子句
- 控制分析
- 關聯失效
- 多字段搜索
- 多重查詢字符串
- 單一查詢字符串
- 最佳字段
- 最佳字段查詢調優
- 多重匹配查詢
- 最多字段查詢
- 跨字段對象查詢
- 以字段為中心查詢
- 全字段查詢
- 跨字段查詢
- 精確查詢
- 模糊匹配
- Phrase matching
- Slop
- Multi value fields
- Scoring
- Relevance
- Performance
- Shingles
- Partial_Matching
- Postcodes
- Prefix query
- Wildcard Regexp
- Match phrase prefix
- Index time
- Ngram intro
- Search as you type
- Compound words
- Relevance
- Scoring theory
- Practical scoring
- Query time boosting
- Query scoring
- Not quite not
- Ignoring TFIDF
- Function score query
- Popularity
- Boosting filtered subsets
- Random scoring
- Decay functions
- Pluggable similarities
- Conclusion
- Language intro
- Intro
- Using
- Configuring
- Language pitfalls
- One language per doc
- One language per field
- Mixed language fields
- Conclusion
- Identifying words
- Intro
- Standard analyzer
- Standard tokenizer
- ICU plugin
- ICU tokenizer
- Tidying text
- Token normalization
- Intro
- Lowercasing
- Removing diacritics
- Unicode world
- Case folding
- Character folding
- Sorting and collations
- Stemming
- Intro
- Algorithmic stemmers
- Dictionary stemmers
- Hunspell stemmer
- Choosing a stemmer
- Controlling stemming
- Stemming in situ
- Stopwords
- Intro
- Using stopwords
- Stopwords and performance
- Divide and conquer
- Phrase queries
- Common grams
- Relevance
- Synonyms
- Intro
- Using synonyms
- Synonym formats
- Expand contract
- Analysis chain
- Multi word synonyms
- Symbol synonyms
- Fuzzy matching
- Intro
- Fuzziness
- Fuzzy query
- Fuzzy match query
- Scoring fuzziness
- Phonetic matching
- Aggregations
- overview
- circuit breaker fd settings
- filtering
- facets
- docvalues
- eager
- breadth vs depth
- Conclusion
- concepts buckets
- basic example
- add metric
- nested bucket
- extra metrics
- bucket metric list
- histogram
- date histogram
- scope
- filtering
- sorting ordering
- approx intro
- cardinality
- percentiles
- sigterms intro
- sigterms
- fielddata
- analyzed vs not
- 地理坐標點
- 地理坐標點
- 通過地理坐標點過濾
- 地理坐標盒模型過濾器
- 地理距離過濾器
- 緩存地理位置過濾器
- 減少內存占用
- 按距離排序
- Geohashe
- Geohashe
- Geohashe映射
- Geohash單元過濾器
- 地理位置聚合
- 地理位置聚合
- 按距離聚合
- Geohash單元聚合器
- 范圍(邊界)聚合器
- 地理形狀
- 地理形狀
- 映射地理形狀
- 索引地理形狀
- 查詢地理形狀
- 在查詢中使用已索引的形狀
- 地理形狀的過濾與緩存
- 關系
- 關系
- 應用級別的Join操作
- 扁平化你的數據
- Top hits
- Concurrency
- Concurrency solutions
- 嵌套
- 嵌套對象
- 嵌套映射
- 嵌套查詢
- 嵌套排序
- 嵌套集合
- Parent Child
- Parent child
- Indexing parent child
- Has child
- Has parent
- Children agg
- Grandparents
- Practical considerations
- Scaling
- Shard
- Overallocation
- Kagillion shards
- Capacity planning
- Replica shards
- Multiple indices
- Index per timeframe
- Index templates
- Retiring data
- Index per user
- Shared index
- Faking it
- One big user
- Scale is not infinite
- Cluster Admin
- Marvel
- Health
- Node stats
- Other stats
- Deployment
- hardware
- other
- config
- dont touch
- heap
- file descriptors
- conclusion
- cluster settings
- Post Deployment
- dynamic settings
- logging
- indexing perf
- rolling restart
- backup
- restore
- conclusion