程式設計界的國小生
- 一、api
-
- 1、概念
- 2、文法
- 3、Demo
-
- 3.1、需求一
- 3.2、需求二
- 二、補充
-
- 1、格式
- 2、優化
- 三、底層原理
-
- 1、問題
- 2、答案
一、api
1、概念
就是批量操作,将多條PUT/POST/DELETE指令合并成一個bulk指令進行操作,節省代碼量也提高性能。
2、文法
PUT /_bulk
{"action":{"metadata"}}
{"data"}
action取值(如下是常用的):
index:普通的PUT操作,可以是建立文檔,也可以是全量替換
create:PUT /index/_doc/id/_create,強制建立
delete:删除一個文檔,隻要1個metadata就可以了,無需{“data”}部分
update:執行的partial update操作
metadata:json,對應的是這部分内容
data:具體操作内容的json,比如
PUT /product/_doc/1
{ "name": "huawei shouji", "desc": "4G 5G", "tags": ["shouji"] }
3、Demo
3.1、需求一
比如要建立一個index名稱為test_index1且_id為1的一個document,document裡面包含如下兩個字段{ "test_field1" : "test1", "test_field2" : "test2" }
PUT /_bulk
{"index" : {"_index" : "test_index1", "_id" : "1"}}
{"test_field1" : "test1", "test_field2" : "test2"}
檢視資料進行驗證
GET /test_index1/_search
{
"query": {
"match_all": {}
}
}
結果:
{
"took" : 5,
"timed_out" : false,
"_shards" : {
"total" : 1,
"successful" : 1,
"skipped" : 0,
"failed" : 0
},
"hits" : {
"total" : {
"value" : 1,
"relation" : "eq"
},
"max_score" : 1.0,
"hits" : [
{
"_index" : "test_index1",
"_type" : "test_type",
"_id" : "1",
"_score" : 1.0,
"_source" : {
"test_field1" : "test1",
"test_field2" : "test2"
}
}
]
}
}
3.2、需求二
綜合應用
POST /_bulk
{"delete":{"_index":"test_index2","_id":"1"}}
{"create":{"_index":"test_index2","_id":"2"}}
{"test_field":"test2"}
{"index":{"_index":"test_index2","_id":"3"}}
{"test_field":"test3"}
{"update":{"_index":"test_index2","_id":"2","retry_on_conflict":3}}
{"doc":{"test_field2":"bulk test"}}
傳回結果是對每一條操作都做一個結果,彼此之間互不影響,也就是說比如第一條delete報錯了,不會影響下面的語句,粗糙了解成“沒mysql的事務控制”。但是再傳回結果裡,會告訴你異常日志。具體傳回結果如下,比如第一個delete是404了。:
{
"took" : 394,
"errors" : false,
"items" : [
{
"delete" : {
"_index" : "test_index2",
"_type" : "_doc",
"_id" : "1",
"_version" : 1,
"result" : "not_found",
"_shards" : {
"total" : 2,
"successful" : 1,
"failed" : 0
},
"_seq_no" : 0,
"_primary_term" : 1,
"status" : 404
}
},
{
"create" : {
"_index" : "test_index2",
"_type" : "_doc",
"_id" : "2",
"_version" : 1,
"result" : "created",
"_shards" : {
"total" : 2,
"successful" : 1,
"failed" : 0
},
"_seq_no" : 1,
"_primary_term" : 1,
"status" : 201
}
},
{
"index" : {
"_index" : "test_index2",
"_type" : "_doc",
"_id" : "3",
"_version" : 1,
"result" : "created",
"_shards" : {
"total" : 2,
"successful" : 1,
"failed" : 0
},
"_seq_no" : 2,
"_primary_term" : 1,
"status" : 201
}
},
{
"update" : {
"_index" : "test_index2",
"_type" : "_doc",
"_id" : "2",
"_version" : 2,
"result" : "updated",
"_shards" : {
"total" : 2,
"successful" : 1,
"failed" : 0
},
"_seq_no" : 3,
"_primary_term" : 1,
"status" : 200
}
}
]
}
二、補充
1、格式
比如嚴格遵守如下格式,多一個回車符号都會報錯
PUT /_bulk
{"action":{"metadata"}}
{"data"}
比如如下會報錯:
PUT /_bulk
{"index" : {"_index" : "test_index1", "_id" : "1"}}
{
"test_field1" : "test1", "test_field2" : "test2"
}
傳回結果
{
"error" : {
"root_cause" : [
{
"type" : "illegal_argument_exception",
"reason" : "Malformed action/metadata line [3], expected START_OBJECT but found [VALUE_STRING]"
}
],
"type" : "illegal_argument_exception",
"reason" : "Malformed action/metadata line [3], expected START_OBJECT but found [VALUE_STRING]"
},
"status" : 400
}
2、優化
bulk size建議大小:
bulk request會加載到記憶體裡,如果太大的話,性能反而會下降,是以需要反複嘗試一個最佳的bulk size。一般從1000~5000條資料開始,嘗試逐漸增加,另外,如果看大小的話,最好是在515MB之間。
三、底層原理
1、問題
為什麼bulk api那麼的醜陋不堪?換行、格式化都會報錯,比如那麼醜陋的格式才能執行?
2、答案
因為bulk中的每組操作(index/create/delete/update)都可能要轉發到不同的node的shard上去執行。
如果采取優雅的json格式,如下:
[
{
"action" : {},
"data" : {}
}
]
首先,整個可讀性非常棒,讀起來很爽,但是ES拿到那種标準格式的JSON串以後,要按照下述流程去進行處理
(1)将JSON數組解析為JSONArray對象,這個時候,整個資料,就會在記憶體中出現一份一模一樣的拷貝,一份資料是JSON文本,一份資料是JSONArray對象。
(2)解析JSON數組裡的每個JSON,對每個請求中的document進行路由
(3)為路由到同一個shard上的多個請求,建立一個請求數組。
(4)将這個請求數組序列化
(5)将序列化後的請求數組發送到對應的節點上去
再看這種醜陋的bulk json格式
{"action" : {"meta"}}
{"data"}
{"action" : {"meta"}}
{"data"}
(1)不用将其轉換為JSON對象,不會出現記憶體中的相同資料的拷貝,直接按照換行符切割JSON
(2)對每兩個一組的json,讀取meta,進行document路由
(3)直接将對應的json發送到node上去
兩種格式對比:
(1)優雅格式:
耗費更多的記憶體,更多的JVM GC開銷
我們之前提到過 bulk size最佳大小的問題,一般建議說在幾千條那樣,然後大小在10MB左右,是以說,可怕的事情來了,假設說現在100個bulk請求發送到了一個節點上去,然後每個請求是10MB,100個請求就是1000MB=1GB。然後每個請求的json都copy一份為JSONArray對象,此時記憶體中的占用就會翻倍,就會占用2GB記憶體,甚至還不止,因為弄成JSONArray後,還可能會多搞一些其他的資料結構,2GB+的記憶體占用。
占用更多的記憶體可能就會積壓其他請求的記憶體使用量,比如說最重要的搜尋請求,分析請求,等等,此時就可能會導緻其他請求的性能急速下降
另外的話,占用記憶體更多,就會導緻ES的java虛拟機的垃圾回收次數更多,更頻繁,每次要回收的垃圾對象更多,耗費的時間更多,導緻ES的java虛拟機停止工作線程的時間更多。
(2)醜陋的JSON格式:
最大的優勢在于,不需要将JSON數組解析為一個JSONArray對象,形成一份大資料的拷貝,浪費記憶體空間,盡可能的保證性能。
微信公衆号
