Written with StackEdit.
ES 不能查询1万条以后的数据
官网说了,有两种解决办法:
search_after
scroll
##search_after的方式
{
"query": {
"match_all": {}
},
"from": 9996,
"size": 5
}
这样查会报错的,from+size超过10000了。
我们使用search_after。 在使用之前我们需要指定一个unique的字段来排序,推荐使用_uid。
{
"query": {
"match_all": {}
},
"sort": {
"_uid": "desc"
},
"from": 9995,
"size": 5
}
这样查询之后返回的结果里就有sort了。
{
"_index":"datasmart",
"_type":"repo",
"_id":"AV5hAJfo_7uB9S_OJQL_",
"_score":null,
"_source":{
"3_t":"mhizam.doe",
"dataType":1,
"dataSource":23,
"1_t":"Muhammad Hizam bin Mohd Ibrahim",
"5_date":1465780654000
},
"sort":[
"repo#AV5hAJfo_7uB9S_OJQL_"
]
}
从最后一个文档里取出sort的值,从10001条数据开始,我们用search_after来查。
{
"query": {
"match_all": {}
},
"sort": {
"_uid": "desc"
},
"search_after": [
"repo#AV5hAJfo_7uB9S_OJQL_"
],
"from": -1,
"size": 5
}
注意加上search_after之后, from 的值要设置成0或者-1。
这样我们就将第10001-10005条数据拿出来了,然后我们取出第10005条数据的_uid。把它放在search_after里就可以查出10005之后的数据了。如此循环就能拿出所有数据了。
限制:
不能自由的跳转到任意一页。但是能同时进行多个查询
它跟scroll的方式差不多,但是不同之处是search_after是无状态的,它总是针对最新版本的搜索进行解析,所以在遍历的时候排序可能会发生变化,这就要看索引的有没有更新和删除操作了。
##scroll的方式
第一次查的时候需要在query string上指定scroll这个参数。 这个参数的意思是告诉ES,“搜索上下文” 应该存活多长时间
注意scroll是有状态的。比如第一次查询的时候根据检索条件查到了2万个文档。在之后scroll的过程中,就算有人添加了几个文档, scroll还是认为只有2万个文档。
curl -XPOST '192.168.40.11:9200/datasmart/repo/_search?scroll=1m&pretty' -H 'Content-Type: application/json' -d'
{
"from":9995,
"size": 5,
"query": {
"match_all" : {}
}
}
'
返回的结果里有一个_scroll_id,下一次查询时要把这个值带上。
{
"_scroll_id" : "DnF1ZXJ5VGhlbkZldGNoAwAAAAAAAChBFk05TnNyQnFCVHpXS2pqVE1TRlBrVkEAAAAAAAAoQBZNOU5zckJxQlR6V0tqalRNU0ZQa1ZBAAAAAAAAKEIWTTlOc3JCcUJUeldLampUTVNGUGtWQQ==",
"took" : 8,
"timed_out" : false,
"_shards" : {
"total" : 3,
"successful" : 3,
"failed" : 0
},
"hits" : {
"total" : 639306,
"max_score" : 1.0,
"hits" : [
{
"_index" : "datasmart",
"_type" : "repo",
"_id" : "AV5WWzBd_7uB9S_OFqo3",
"_score" : 1.0,
"_source" : {
"description" : "Key Distribution Center Service Account",
"displayName" : null
}
},
...
]
}
}
拿出接下来的5条数据:
检索条件第一次查询的时候已经定了,这里就不需要加任何检索条件了。
这样看来,使用scroll的方式要判断一下,当from+size超过1万条的时候才开始scroll。一旦滚动起来,这个size是不可更改的。所以到1万的时候要不要考虑一下把size的值改大,或者改小。
注意这个时候url里也不能再有index和type name了。scorll:1m表示 让ES把搜索上下文再保活1分钟。
由于每一次请求都会设置一个新的失效时间,所以一般情况下这个值不需要设置的太大。
curl -XPOST '192.168.40.11:9200/_search/scroll?pretty' -H 'Content-Type: application/json' -d'
{
"scroll" : "1m",
"scroll_id" : "DnF1ZXJ5VGhlbkZldGNoAwAAAAAAAChBFk05TnNyQnFCVHpXS2pqVE1TRlBrVkEAAAAAAAAoQBZNOU5zckJxQlR6V0tqalRNU0ZQa1ZBAAAAAAAAKEIWTTlOc3JCcUJUeldLampUTVNGUGtWQQ=="
}
'
返回接下来的5条数据:
{
"_scroll_id" : "DnF1ZXJ5VGhlbkZldGNoAwAAAAAAAChBFk05TnNyQnFCVHpXS2pqVE1TRlBrVkEAAAAAAAAoQBZNOU5zckJxQlR6V0tqalRNU0ZQa1ZBAAAAAAAAKEIWTTlOc3JCcUJUeldLampUTVNGUGtWQQ==",
"took" : 1,
"timed_out" : false,
"terminated_early" : true,
"_shards" : {
"total" : 3,
"successful" : 3,
"failed" : 0
},
"hits" : {
"total" : 639306,
"max_score" : 1.0,
"hits" : [
{
"_index" : "datasmart",
"_type" : "repo",
"_id" : "AV5WWzBd_7uB9S_OFqpD",
"_score" : 1.0,
"_source" : {
"description" : null,
"displayName" : "Microsoft Exchange Approval Assistant"
}
},
...
]
}
}
scroll 完毕我们还要把搜索上下文清除,一般不需要刻意清除,因为失效时间一到,ES会自动清除搜索上下文。
查看所有搜索上下文:
curl -XGET '192.168.40.11:9200/_nodes/stats/indices/search?pretty'
清除scroll:
curl -XDELETE '192.168.40.11:9200/_search/scroll?pretty' -H 'Content-Type: application/json' -d'
{
"scroll_id" : ["DnF1ZXJ5VGhlbkZldGNoAwAAAAAAAChBFk05TnNyQnFCVHpXS2pqVE1TRlBrVkEAAAAAAAAoQBZNOU5zckJxQlR6V0tqalRNU0ZQa1ZBAAAAAAAAKEIWTTlOc3JCcUJUeldLampUTVNGUGtWQQ=="]
}
'
方括号里可以填入多个scroll id.
清除所有scroll:
curl -XDELETE '192.168.40.11:9200/_search/scroll/_all?pretty'
如果文档数太多的话,可以将scroll切分成多片。
感觉全库导出的时候用这个比较好。这个其实就是多个scroll同时跑而已,用法上跟scroll几乎一模一样。
发2次请求得到两个scroll_id, 然后每个slice使用自己的scroll_id去读即可。
curl -XGET '192.168.40.11:9200/datasmart/repo/_search?scroll=1m&pretty' -H 'Content-Type: application/json' -d'
{
"slice": {
"id": 0,
"max": 2
},
"query": {
"match_all" : {}
}
}
'
curl -XGET '192.168.40.11:9200/datasmart/repo/_search?scroll=1m&pretty' -H 'Content-Type: application/json' -d'
{
"slice": {
"id": 1,
"max": 2
},
"query": {
"match_all" : {}
}
}
'
注意 如果slice的个数比 shards的个数大。 那么第一次调用的时候, slice filter会非常慢。调用几次后, filter应该已经被缓存了,所以接下来的调用应该会快很多。 但是你应该限制 同时跑slice的个数以避免内存爆炸。