批量操作方式

  • 批量查询 _mget
  • 查询不同index,不同 type 下的多条数据
  • 查询同一个index下多条数据
  • 同一个index同一type下查询多条数据
  • 批量增删改 _bulk
  • _bulk 语法
  • bulk 能够操作的类型
  • 实际的使用案例如下
  • 批量操作说明
  • 奇怪语法的优势


批量查询 _mget

批量查询使用的 _mget 接口,通过批量查询,一次性可以获取很多数据,减少客户端和ES服务之间的网络请求,对客户端的响应速度有极大的帮助

查询不同index,不同 type 下的多条数据

在docs数组中,要查询的每一个json格式指定要查询数据的 metadata

GET /_mget
{
  "docs":[
      {
        "_index":"ecommerce",
        "_type":"product",
        "_id":1
      },
      {
        "_index":"test_index",
        "_type":"test_index",
        "_id":1
      }
    ]
}

查询同一个index下多条数据

如果是同一个 index 下的数据,那么在查询api的接口中,先指定索引名称,在要查询的数据中,把剩余的元数据补足就好

GET /ecommerce/_mget
{
  "docs":[
      {
        "_type":"product",
        "_id":1
      },
      {
        "_type":"product",
        "_id":2
      }
    ]
}

同一个index同一type下查询多条数据

GET /ecommerce/product/_mget
{
  "ids":[1,2,3]
}

批量增删改 _bulk

_bulk 语法

语法格式如下

PUT /_bulk
{"action": {"metadata"}}
{"data"}
{"action": {"metadata"}}
{"data"}

在通过 bulk 进行批量操作的时候,两行为一组,第一行操作为元数据,第二行为操作的具体数据,如果是 delete 操作的话,只是需要一行即可。

bulk 能够操作的类型

  1. delete:删除一个文档,只要1个json串就可以了
  2. create:PUT /index/type/id/_create,强制创建
    index:普通的put操作,可以是创建文档,也可以是全量替换文档
    update:执行的partial update操作

实际的使用案例如下

PUT /_bulk
{"delete":{"_index":"test_index","_type":"test_index","_id":1}}
{"create":{"_index":"test_index","_type":"test_index","_id":2}}
{"name":"test1","num":"1","tags":[]}
{"index":{"_index":"test_index","_type":"test_index","_id":3}}
{"name":"test2","num":2,"tags":["aaa","vvv"]}
{"index":{"_index":"test_index","_type":"test_index","_id":5}}
{"name":"replace test5","tags":["ccc","ddd"]}
{"update":{"_index":"test_index","_type":"test_index","_id":6}}
{"doc":{"name":"replace test6"}}

上面的语句分析:
第一行:删除某一条数据
第二行和第三行:新增一条数据
第四行和第五行:新增一条数据的索引
第六行和第七行:新增一条数据的索引
第八行和第九行:部分修改某一条数据

批量操作说明

  • 语法上,bulk api对json的语法,有严格的要求,每个json串不能换行,只能放一行
  • 同时一个json串和一个json串之间,必须有一个换行
  • 每一组语句的执行成功与否,不影响其他组结果,成功与否会在结果中进行返回
  • bulk操作中,任意一个操作失败,是不会影响其他的操作的,但是在返回结果里,会告诉你异常日志
  • bulk size最佳大小设置
    bulk request会加载到内存里,如果太大的话,性能反而会下降,因此需要反复尝试一个最佳的bulk size。一般从1000~5000条数据开始,尝试逐渐增加。另外,如果看大小的话,最好是在5 ~ 15MB之间。
  • 同 _mget 一样,语法有一些变化形式,指定 _index 以及 _type 时,可以减少json的大小

奇怪语法的优势

  1. bulk中的每个操作都可能要转发到不同的node的shard去执行
  2. 如果采用比较良好的json数组格式

允许任意的换行,整个可读性非常棒,读起来很爽,es拿到那种标准格式的json串以后,要按照下述流程去进行处理

a. 将json数组解析为JSONArray对象,这个时候,整个数据,就会在内存中出现一份一模一样的拷贝,一份数据是json文本,一份数据是JSONArray对象
b 解析json数组里的每个json,对每个请求中的document进行路由
c 为路由到同一个shard上的多个请求,创建一个请求数组
d 将这个请求数组序列化
e 将序列化后的请求数组发送到对应的节点上去

  1. 好格式会耗费更多内存,更多的jvm gc开销
    前面提到过bulk size最佳大小的那个问题,一般建议说在几千条那样,然后大小在10MB左右,所以说,可怕的事情来了。假设说现在100个bulk请求发送到了一个节点上去,然后每个请求是10MB,100个请求,就是1000MB = 1GB,然后每个请求的json都copy一份为jsonarray对象,此时内存中的占用就会翻倍,就会占用2GB的内存,甚至还不止。因为弄成jsonarray之后,还可能会多搞一些其他的数据结构,2GB+的内存占用。
    占用更多的内存可能就会积压其他请求的内存使用量,比如说最重要的搜索请求,分析请求,等等,此时就可能会导致其他请求的性能急速下降
    另外的话,占用内存更多,就会导致java虚拟机的垃圾回收次数更多,跟频繁,每次要回收的垃圾对象更多,耗费的时间更多,导致es的java虚拟机停止工作线程的时间更多
  2. 现在的奇特格式好处

{“action”: {“meta”}}
{“data”}
{“action”: {“meta”}}
{“data”}

a 不用将其转换为json对象,不会出现内存中的相同数据的拷贝,直接按照换行符切割json
b 对每两个一组的json,读取meta,进行document路由
c 直接将对应的json发送到node上去

  1. 最大的优势在于,不需要将json数组解析为一个JSONArray对象,形成一份大数据的拷贝,浪费内存空间,尽可能地保证性能