一、问题背景
最近在做ES清空指定index下的指定type类型数据时,发现在大数据量情况下删除的时候会比较慢,会超过自己设定的socketTimeout时间,需要提升性能。
二、集群环境
ElasticSearch版本: 阿里云 5.5.3
删除的数据量大小:7000w
三、原始日志
可以看到日志打印时间从13:44:11-14:14:16,删除30分钟后,程序抛出sockectTimeout异常
四、代码
获取ES restClient 代码,参考阿里云给的示例 :Java REST Client
public static RestClient getClient(Configuration config){
final CredentialsProvider credentialsProvider = new BasicCredentialsProvider();
credentialsProvider.setCredentials(AuthScope.ANY,
new UsernamePasswordCredentials(config.getString(Key.USERNAME), config.getString(Key.PASSWORD)));
final RequestConfig reqConf = RequestConfig.custom().setConnectTimeout(60*1000)
.setSocketTimeout(30*60*1000)
.setConnectionRequestTimeout(30*1000).build();
RestClient client = RestClient.builder(getHosts(config)).setHttpClientConfigCallback(new RestClientBuilder.HttpClientConfigCallback() {
@Override
public HttpAsyncClientBuilder customizeHttpClient(HttpAsyncClientBuilder httpClientBuilder) {
httpClientBuilder.setDefaultRequestConfig(reqConf);
return httpClientBuilder.setDefaultCredentialsProvider(credentialsProvider);
}
}).setMaxRetryTimeoutMillis(60*60*1000).build();
return client;
}
可以看到上边的代码中设定的socketTimout时间为30分钟。接下来可以看到删除ES下指定index下的指定type代码段:
LOG.info("执行es清空开始");
String esIndex = originalConfig.getString(Key.INDEX);
String esType = originalConfig.getString(Key.TYPE);
String path = esIndex + "/" + esType + "/_delete_by_query";
RestClient client = null;
try {
client = EsClientUtil.getClient(originalConfig);
HttpEntity httpEntity = new StringEntity(Key.queryAll, "UTF-8");
Response response = client.performRequest("POST", path , Collections.<String, String>emptyMap(), httpEntity, new BasicHeader(HTTP.CONTENT_TYPE,"application/json"));
StatusLine line = response.getStatusLine();
LOG.info("清空es返回结果 {}", JSON.toJSONString(line));
if(line != null && line.getStatusCode() == Key.SUCCESS ){
LOG.info("执行清空es成功");
}
}catch (Exception e){
LOG.error("执行清空es出现异常信息",e);
}finally {
if(client != null){
try {
client.close();
} catch (IOException e) {
LOG.error("关闭Es RestClient失败",e);
}
}
}
LOG.info("执行清空es结束");
主要是使用delete_by_query实现的。
因此有2种方式:1、调大超时时间,但这个显然不治标 2、需要看一下这个API上是否有可以调优的参数设置
参考官网给出的API说明文档:Delete by query API
发现了2个可以进行调优的参数:
每次es删除时,默认是先从上到下查询出100条记录然后再进行删除。
可以理解为,默认值是一个线程在进行查询数据并删除,当设置这个slices值时,会将es下的数据进行切分,启动多个task去做删除,理解为多线程执行操作 。
五、调优
在代码里加上找到的调优参数:
LOG.info("执行es清空开始");
String esIndex = originalConfig.getString(Key.INDEX);
String esType = originalConfig.getString(Key.TYPE);
int size = originalConfig.getInt(Key.DEL_SCROLL_SIZE, DEFAULT_SCROLL_SIZE);
int sliceSize = originalConfig.getInt(Key.ES_SLICE_SIZE, DEFAULT_SLICES);
// conflicts=proceed参数,指的是当删除遇到版本冲突时,继续删除;默认不写,es遇到冲突时会直接终止。
String path = esIndex + "/" + esType + "/_delete_by_query?conflicts=proceed&scroll_size=" + size + "&slices=" + sliceSize;
RestClient client = null;
try {
client = EsClientUtil.getClient(originalConfig);
HttpEntity httpEntity = new StringEntity(Key.queryAll, "UTF-8");
LOG.info("清空es请求路径为 {}", path);
Response response = client.performRequest("POST", path , Collections.<String, String>emptyMap(), httpEntity, new BasicHeader(HTTP.CONTENT_TYPE,"application/json"));
StatusLine line = response.getStatusLine();
LOG.info("清空es返回结果 {}", JSON.toJSONString(line));
if(line != null && line.getStatusCode() == Key.SUCCESS ){
LOG.info("执行清空es成功");
}
}catch (Exception e){
LOG.error("执行清空es出现异常信息",e);
}finally {
if(client != null){
try {
client.close();
} catch (IOException e) {
LOG.error("关闭Es RestClient失败",e);
}
}
}
LOG.info("执行清空es结束");
测试数据
scroll_size | slices | 耗时 |
3000 | 2 | >30min |
3000 | 5 | 3min16s |
3000 | 10 | 5min59s 这个记录有待考量,与底下对比不符 |
5000 | 5 | 3min33s |
5000 | 10 | 3min34s |
10000 | 5 | 5min01s |
10000 | 10 | 4min50s |
综上可以看出,增加这2个参数的性能提升很明显,从原来的大于30min缩短到3min左右,性能提升了10倍左右。
六、总结
对于调用第三方服务API时,需要多加注意可能存在的性能问题,然后也需要更加细致去看提供的文档,很多东西其实文档里都有写。