如何优雅的全量读取Elasticsearch索引里面的数据

### (一)scroll的介绍

有时候我们可能想要读取整个es索引的数据或者其中的大部分数据,来重建索引或者加工数据,相信大多数人都会说这很简单啊直接用from+size就能搞定,但实际情况是from+size的分页方法不适合用于这种全量数据的抽取,越到后面这种方法的性能就越低,这也是es里面为什么限制了单次查询结果的数据不能超过1万条数据的原因。


es里面提供了scroll的方式来全量读取索引数据其与数据库里面的游标(cursor)的概念非常类似,使用scroll读取数据的时候,只需要发送一次查询请求,然后es服务端会生成一个当前请求索引的快照数据集,接着我们每次通过scrollId来读取指定大小的批次数据,直到把整个索引的数据读取完毕。

这里面需要注意,当索引快照集生成的时候,其实在es内部维护了一个search context的上下文,这个上下文在指定的时间间隔内是只读的和不可变的,也就是只要它生成,那么后续你的添加,删除,更新操作的数据都不会被感知。


### (二)scroll的使用

下面看下如何使用:


(1)要使用scroll方式来读取数据,需要两步操作,第一步先做一个search context的初始化操作,如下命令:
````
curl -XGET 'localhost:9200/twitter/tweet/_search?scroll=1m' -d '
{
"query": {
"match" : {
"title" : "elasticsearch"
}
}
}
'
````


注意上面url里面的scroll=1m代表,这个search context只保留一分钟的有效期。

(2)在第一步操作里面我们能够获取一个scrollId,然后后面的每个读取都会得到一个scrollId,我们在读取next批次的数据要把这个scrollId回传,如下:

````
curl -XGET 'localhost:9200/_search/scroll' -d'
{
"scroll" : "1m",
"scroll_id" : "c2Nhbjs2OzM0NDg1ODpzRlBLc0FXNlNyNm5JWUc1"
}
'
````


或者通过search lite api的方式:

````
curl -XGET 'localhost:9200/_search/scroll?scroll=1m' -d 'c2Nhbjs2OzM0NDg1ODpzRlBLc0FXNlNyNm5JWUc1'

````


这样依次循环读取直到searchHits数组为空的情况下就代表数据读取完毕。


同理聚合的scroll请求,也是如此,但聚合请求的数据体只会在初始化的search里面存在,这一点需要注意,不过聚合请求的scroll一般没有这种应用场景,毕竟聚合后的结果一般都是少了好几个数量级的。


此外scroll请求还可以添加一个或多个排序字段,如果你读取的索引数据完全忽略它的顺序,那么我们还可以使用doc字段排序来提升性能。

````
curl -XGET 'localhost:9200/_search?scroll=1m' -d '
{
"sort": [
"_doc"
]
}
'
````



ok,再补充下再java api里面如何全量读取es索引数据的方法:

````
` //指定一个index和type
SearchRequestBuilder search = client.prepareSearch("active2018").setTypes("active");
//使用原生排序优化性能
search.addSort("_doc", SortOrder.ASC);
//设置每批读取的数据量
search.setSize(100);
//默认是查询所有
search.setQuery(QueryBuilders.queryStringQuery("*:*"));
//设置 search context 维护1分钟的有效期
search.setScroll(TimeValue.timeValueMinutes(1));

//获得首次的查询结果
SearchResponse scrollResp=search.get();
//打印命中数量
System.out.println("命中总数量:"+scrollResp.getHits().getTotalHits());
//打印计数
int count=1;
do {
System.out.println("第"+count+"次打印数据:");
//读取结果集数据
for (SearchHit hit : scrollResp.getHits().getHits()) {
System.out.println(hit.getSource()) ;
}
count++;
//将scorllId循环传递
scrollResp = client.prepareSearchScroll(scrollResp.getScrollId()).setScroll(TimeValue.timeValueMinutes(1)).execute().actionGet();

//当searchHits的数组为空的时候结束循环,至此数据全部读取完毕
} while(scrollResp.getHits().getHits().length != 0);
````



### (三)删除无用的scroll


上文提到scroll请求时会维护一个search context快照集,这是如何做到的? 通过前面的几篇文章(点底部菜单栏可以看到),我们知道es在写入数据时,会在内存中不断的生成segment,然后有一个merge线程,会不断的合并小segment到更大的segment里面,然后再删除旧的segment,来减少es对系统资源的占用, 尤其是文件句柄,那么维护一个时间段内的索引快照,则意味着这段时间内的所有segment不能被合并,否则就破坏了快照的静态性,这样以来暂时不能被合并的小segment会占系统大量的文件句柄和系统资源,所以scroll的方式一定是离线使用的而不是提供给近实时使用的。

我们需要养成一个好习惯,当我们用完之后应该手动清除scroll,虽然search context超时也会自动清除。


es中提供了可以查看当前系统中有多少个open search context的api命令:

````
curl -XGET localhost:9200/_nodes/stats/indices/search?pretty
````



下面看下删除scrollId的方式

(1)删除一个scrollId

````
DELETE /_search/scroll
{
"scroll_id" : "UQlNsdDcwakFMNjU1QQ=="
}

````


(2)删除多个scrollId

````
DELETE /_search/scroll
{
"scroll_id" : [
"aNmRMaUhiQlZkMWFB==",
"qNmRMaUhiQlZkMWFB=="
]
}


````


(3)删除所有的scrollId
````
DELETE /_search/scroll/_all
````



(4)search lite api的删除多个scrollId用法

````
DELETE /_search/scroll/aNmRMaUhiQlZkMWFB==,qNmRMaUhiQlZkMWFB==
````



上面的所有的功能在es2.3.4的版本中已经验证过,此外在es5.x之后的版本中,还增加了一个分片读取索引的功能,通过分片支持并行的读取方式,来提高导出效率:


一个例子如下:
````
GET /twitter/_search?scroll=1m
{
"slice": {
"id": 0,
"max": 2
},
"query": {
"match" : {
"title" : "elasticsearch"
}
}
}
GET /twitter/_search?scroll=1m
{
"slice": {
"id": 1,
"max": 2
},
"query": {
"match" : {
"title" : "elasticsearch"
}
}
}
````


注意上面的slice参数,里面id字段代表当前读取的按个分片的数据,max参数代表我们将整个索引数据切分成分片的个数,默认的分片算法:

````
slice(doc) = floorMod(hashCode(doc._uid), max)
````


从上面能看到是基于uid字段的hashCode与分片的最大个数求模得出来的,注意floorMod方法与%求模在都是正整数的情况下结果是一样的。


slice字段还可以加入自定义的字段参与分片,比如基于日期字段:
````
"slice": {
"field": "date",
"id": 0,
"max": 10
}
````
参与分片的字段必须是数值字段并需要开启doc value,另外设置的max数量最好不要超过shard的个数,否则查询性能会下降,默认es对每个索引限制的最大分片量是1024,不过在setting里面通过设置index.max_slices_per_scroll参数改变。

### (四)总结

本篇文章介绍了如何优雅的全量读取es的索引数据以及它的一些原理和注意事项,了解这些有助于我们在日常工作中更好的使用es,从而提升我们对es的认知。

[b][color=green][size=large] 有什么问题可以扫码关注微信公众号:我是攻城师(woshigcs),在后台留言咨询。 技术债不能欠,健康债更不能欠, 求道之路,与君同行。 [/size][/color][/b] [img]http://dl2.iteye.com/upload/attachment/0104/9948/3214000f-5633-3c17-a3d7-83ebda9aebff.jpg[/img]
  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Elasticsearch 是一个开源的分布式搜索和分析引擎,用于快速存储、搜索和分析大量的数据。在 Elasticsearch 中,我们可以通过索引数据来建立搜索的数据集。 上传索引数据是指将数据导入到 Elasticsearch 中的过程。可以通过多种方式来上传索引数据,下面是一些常用的方法: 1. 使用 Elasticsearch 提供的 RESTful API:通过调用 Elasticsearch 的 RESTful API,我们可以将数据以 JSON 格式的文档插入到索引中。首先,我们需要创建一个索引,并指定索引名称、数据类型和字段的映射规则。然后,可以使用 API 中的索引命令来插入数据。 2. 使用 Logstash:Logstash 是一个开源的数据处理工具,它可以从各种来源(如文件、数据库、Kafka 等)读取数据,并将其转换为适合 Elasticsearch 的格式,然后将数据导入到 Elasticsearch 中。Logstash 支持多种输入和输出插件,可以根据需要来选择适合的插件。 3. 使用 Filebeat:Filebeat 是一个轻量级的日志数据收集器,它可以监控文件或目录中的变化,并将变化的数据发送到 Elasticsearch 或 Logstash 进行处理。通过配置 Filebeat,可以指定要监控的文件和数据格式,并将其传输到 Elasticsearch 中。 4. 使用批量处理 API:Elasticsearch 提供了一个批量处理 API,可以在一次请求中插入多个文档。可以将多个文档放在一个 JSON 数组中,然后使用批量处理 API 将整个数组上传到 Elasticsearch 中。 上传索引数据Elasticsearch 中的一个重要步骤,能够帮助我们构建强大的搜索和分析功能。通过以上方法,我们可以将不同来源和格式的数据上传到 Elasticsearch 中,以便后续的数据分析和搜索操作。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值