elasticsearch快速批量导出数据
前言
最近遇到一个需求,从es批量导出数据.开始使用scoll单线程导出,速度不是很理想.然后又根据一个时间字段进行分片,然后使用多线程导出,速度也一般.最后在es官网找到了scoll的进阶版:scoll slice,遂用这个写了个多线程导出的程序,最终导出的速度在1天100亿一、elasticsearch scoll slice是什么?
scoll slice就是在scoll的基础上,将每一个scoll划分为多个切片,每个切片都可以独立的进行使用. 切片的数量是索引分片数量的整数倍时效果较好.二、代码实现
ScollSliceThread:相关代码::
SearchSourceBuilder builder = new SearchSourceBuilder();
SliceBuilder sliceBuilder = new SliceBuilder(slice, maxSlice);
builder.size(10000).slice(sliceBuilder).query(QueryBuilders.matchAllQuery());
List<Object> dataList = new ArrayList<>();
while (true) {
try {
long start = System.currentTimeMillis();
SearchResponse response = scollSliceSearch(builder);
scollId = response.getScrollId();
for (SearchHit hit : response.getHits()) {
Map<String, Object> sourceAsMap = hit.getSourceAsMap();
dataList.add(sourceAsMap);
}
if(response.getHits().getHits().length < 10000){
// TODO 处理:
activeCount.decrementAndGet();
log.info("本线程执行完毕");
break;
}
}catch (Exception e) {
// 忽略
}
}
三、注意事项
- 每个索引slice的数量最好是分片数量(每个索引开几个线程),下面代码可以根据索引名获取分片数量(不是别名,如果是别名还需处理)
// 由于 scoll slice的分区数量是和索引的分片数量有关,所以此处需要计算最佳分区数量
protected void calculateSliceCount() throws IOException {
GetSettingsRequest request = new GetSettingsRequest();
request.indices(indices)
.indicesOptions(IndicesOptions.fromOptions(true,
true, true,
true));
GetSettingsResponse response = client.indices().getSettings(request, RequestOptions.DEFAULT);
ImmutableOpenMap<String, Settings> settingMap = response.getIndexToSettings();
Settings setting = settingMap.get(indices);
if(setting == null){
maxSlice = 0;
}else{
String shardCount = setting.get("index.number_of_shards");
maxSlice = Integer.valueOf(shardCount);
}
// 将maxSlice设置为shardCount数量以获取最佳性能
log.info("indices:{}, shard count:{}", indices, maxSlice);
}
- 第一次进行切片的时候会比较久,可能出现超时的情况
- 如果涉及到的索引数量过多(线程池线程过多),导出的性能会下降,与服务器的IO有关
- 如何判断所有的线程都已经执行完毕:使用AtomicInteger进行线程计数,主线程中监听计数器,当计数器为0时,表示所有的线程全部执行完毕