[Toc]
1、bulk的執(zhí)行過程
一次bulk請求可能包含了多個增刪改document的操作,因此bulk的每個操作都可能要轉(zhuǎn)發(fā)到不同的es node的primary shard去執(zhí)行,這個過程就包含了json數(shù)據(jù)的傳輸。
2、假如采用比較良好的json數(shù)組格式
假如使用比較良好的json數(shù)組格式,會是這個樣子
POST /_bulk
{ "update": { "_index": "test_index", "_type": "test_type", "_id": "1", "_retry_on_conflict" : 3} }
{
"doc": {
"test_field": "update test"
}
}
這樣看起來是不是清晰明了?但es是不支持的。因為假如es支持的話,es需要對json數(shù)組進行額外處理,整個_bulk流程是下面這樣的
(1) 將良好的json數(shù)組格式解析為JSONArray對象,這個時候,整個json就會在內(nèi)存中出現(xiàn)一份一模一樣的拷貝,一份是json文本,一份是JSONArray對象
(2) 解析出json數(shù)組里的每個json(也就是document)
(3) 對每個請求的document進行路由
(4) 為路由到同一個shard上的多個請求,創(chuàng)建一個請求數(shù)組
(5) 將這個請求數(shù)組序列化
(6) 將序列化后的請求數(shù)組發(fā)送到對應(yīng)的節(jié)點上去
之前提到過bulk size最佳大小的問題,一般建議在幾千條或者10MB左右。所以說可怕的事情來了,假如有100個bulk請求發(fā)送到一個節(jié)點上去,然后每個請求是10MB,100個請求就是1000M=1G,然后每個請求的json都copy一份為JSONArray對象,此時內(nèi)存占用就會翻倍,共占用2G內(nèi)存,甚至不止,因為弄成JSONArray之后,還可能會搞一些其它的數(shù)據(jù)結(jié)構(gòu),就會占用2G+的內(nèi)存。
占用更多的內(nèi)存可能就會積壓內(nèi)存,影響其它請求的內(nèi)存使用量,比如說最重要的搜索請求、搜索請求等等,此時就可能導致其它請求的性能急速下降。
另外,占用內(nèi)存更多,也會導致java虛擬機的垃圾回收次數(shù)更頻繁,每次要回收的垃圾對象更多,耗費時間更多,導致es的java虛擬機阻塞工作線程的時間更多
3、es支持的json格式
bulk的格式要求為一條數(shù)據(jù)的json要放在一行
POST /_bulk
{ "update": { "_index": "test_index", "_type": "test_type", "_id": "1", "_retry_on_conflict" : 3} }
{ "doc" : {"test_field" : "update test"} }
這樣做有什么好處呢?我們來看下整個_bulk流程
(1) 不用將其轉(zhuǎn)換為json對象,不會出現(xiàn)內(nèi)存中相同數(shù)據(jù)的拷貝,直接按照換行符切割json
(2) 對每兩個一組的json,讀取meta,進行document路由
(3) 直接將對應(yīng)的json發(fā)送到node上去
其最大優(yōu)勢在于,不需要講json數(shù)組解析為一個JSONArray對象,形成一份大數(shù)據(jù)的拷貝,浪費內(nèi)存空間,盡可能地保證性能