在ElasticSearch中,每建立一个索引,至少需要一个主分片和一个副分片。当索引过多的时候,在不加机器的情况下,是非常影响性能的。那有什么办法可以避免不加机器也能提高性能了。当然有:那就是删除一些基本不可能用到的,且索引下文档数特别少的索引。可删除后,有一天需要回复怎么办了,备份起来是个好办法。即:将ES中的数据备份到本地,然后将其压缩。
索引导出为JSON文本后,高度结构化。因为JSON是K-V形式的,导致key可以无限重复,这样的数据最适合压缩存储,我导出一个索引大概有1.1G
,将其压缩后就只有80MB
了。
如图:JSON中每一组数据都有memberId,这样势必造成空间的浪费,压缩后自然可以回避这个问题。
压缩实现及其解读:
/** es请求客户端:每个人的用法不一样,此处需要自己去构建链接 **/
private RestHighLevelClient client;
/**
* @param indexName
* 索引名称
* @throws IOException
* IO异常
*/
public void exportindex(String indexName) throws IOException {
String path = "src/main/resources/indexTemp/";
File file = new File(path);
if (!file.exists()) {
file.mkdirs();
}
//构建路径
path = path + indexName + ".json";
// 设定滚动时间间隔
Scroll scroll = new Scroll(TimeValue.timeValueMinutes(2L));
// 查询全部
SearchRequest searchRequest = new SearchRequest(indexName);
searchRequest.scroll(scroll);
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
// 每次返回1000条数据
searchSourceBuilder.size(Constant.NUMBER_1000);
// 备份索引中字段信息(如果不写,默认备份全部,这里需要备份全部,写的话就是备份数组中的字段)
// searchSourceBuilder.fetchSource(new String[] { "id", "memberId", "realName" }, new String[] {});
// bool查询
BoolQueryBuilder boolBuilder = QueryBuilders.boolQuery();
searchSourceBuilder.query(boolBuilder);
searchRequest.source(searchSourceBuilder);
SearchResponse searchResponse = client.search(searchRequest, RequestOptions.DEFAULT);
// 滚动id
String scrollId = searchResponse.getScrollId();
// scrollId使用完之后手动清除,减少内存使用量,当然也可以不清理,es会自动清理的,但是没有手动清理好
ClearScrollRequest clearScrollRequest = new ClearScrollRequest();
clearScrollRequest.addScrollId(scrollId);
// 获取命中数组,一要注意两个getHits()方法,第一个是获取SearchHits对象,第二个是获取SearchHit数组
SearchHit[] hits = searchResponse.getHits().getHits();
// 将json格式的数据导出到文件中;异常一行的些
try (BufferedWriter out = new BufferedWriter(new FileWriter(path, true));) {
for (SearchHit hit : hits) {
// 读取为String
String json = hit.getSourceAsString();
out.write(json);
out.write("\r\n");
out.flush();
}
// 搜索完第一个1000条后往下继续执行
while (Objects.nonNull(hits) && hits.length > 0) {
SearchScrollRequest scrollRequest = new SearchScrollRequest(scrollId);
// 再次设置滚动时间
scrollRequest.scroll(scroll);
// 执行滚动查询
searchResponse = client.scroll(scrollRequest, RequestOptions.DEFAULT);
// 获取滚动id
scrollId = searchResponse.getScrollId();
// 加入清除
clearScrollRequest.addScrollId(scrollId);
// 返回命中
hits = searchResponse.getHits().getHits();
if (Objects.nonNull(hits) && hits.length > 0) {
for (SearchHit hit : hits) {
// 读取为String
String json = hit.getSourceAsString();
out.write(json);
out.write("\r\n");
out.flush();
}
}
}
// 最后执行清除:crollId使用完之后手动清除,减少内存使用量,当然也可以不清理,es会自动清理的,但是没有手动清理好
ClearScrollResponse clearScrollResponse = client.clearScroll(clearScrollRequest, RequestOptions.DEFAULT);
LOG.info("备份索引结果为:{}", clearScrollResponse.isSucceeded());
} catch (Exception e) {
LOG.error("备份索引【{}】失败", indexName, e);
}
}
执行后,json文本会导出到resources目录下,最后压缩一下,大大的节省空间。压缩方法参考: 导出数据库表为sql文件,并进行压缩.
警示:
本文是从是一个开发的角度出发,将ES的数据读取出来,然后写到本地,最后进行压缩。索引所占空间如果只有一两个G的大小,是完全没有问题的,大于两个G就最好直接让运维人员将ES数据从服务器上拉取出来,本文的方法也就不是很实用了。