编程界的小学生
一、分页API
1、api
from:从第几条开始
size:每页几条
2、demo
2.1、查询前两条数据
GET /_search?size=2
或
GET /_search?from=0&size=2
2.2、查询第50~60条数据
GET /_search?from=50&size=10
二、deep paging
1、描述
就是深度分页,假设有6w条数据平分到了3个shard中,假设需求是分页,数据太多了,要查第1000页的数据,假设每页10条,第1000页的数据也就是第1w条。由于6w条数据,3个shard,所以大约每个shard上2w条数据。那么我们客观的认为应该是协调节点从三个shard中分别拿出10条数据(第10001到10010这十条),然后再协调节点中将这30条数据根据规则获取想要的前10条。但是我们的想法很天真,实际ES的做法是将三个shard上的1~10010条数据分别返回给协调节点,也就是协调节点最终的结果是30030条数据,在这30030条数据的基础上进行按照规则(比如先排序然后再分页取前10)返回给客户端,所以造成了性能的浪费和瓶颈所在。
2、图解
三、scroll解决方案
1、场景
就跟js的懒加载一样,滚动加载。也可以用于解决深度分页问题。
2、原理
scroll搜索会在第一次搜索的时候,保存一个当时的试图快照。也就是保存完快照后之后的更改都不会对这个快照的数据产生影响,你改你的,我查的还是老的。
默认采用基于_doc的创建时间来排序,不使用_score进行排序的方式,性能较高。
3、用法
3.1、数据准备
PUT /product/_doc/1
{
"name": "xiaomi shouji",
"desc": "niubi quanwangtong",
"tags": ["niubi", "quanwangtong", "xiaomi", "shouji"]
}
PUT /product/_doc/2
{
"name": "huawei shouji",
"desc": "4G 5G",
"tags": ["shouji"]
}
PUT /product/_doc/3
{
"name": "xiaomi shouhuan",
"desc": "quanzidong",
"tags": ["shengdian", "xiaomi", "shouji"]
}
3.2、scroll api
GET /product/_search?scroll=1m
{
"query": {
"match_all": {}
},
"size": 1
}
返回结果
{
"_scroll_id" : "DXF1ZXJ5QW5kRmV0Y2gBAAAAAAAAQoAWOWV3WnBTMVNSNFc1ZXAzeGxlWGJ1UQ==",
"took" : 0,
"timed_out" : false,
"_shards" : {
"total" : 1,
"successful" : 1,
"skipped" : 0,
"failed" : 0
},
"hits" : {
"total" : {
"value" : 3,
"relation" : "eq"
},
"max_score" : 1.0,
"hits" : [
{
"_index" : "product",
"_type" : "_doc",
"_id" : "1",
"_score" : 1.0,
"_source" : {
"name" : "xiaomi shouji",
"desc" : "niubi quanwangtong",
"tags" : [
"niubi",
"quanwangtong",
"xiaomi",
"shouji"
]
}
}
]
}
}
获取的结果会有一个scroll_id,下一次在发送scroll请求时,必须带上这个scroll_id,因为只有这样,才会去查询上一次scroll生成的快照。
GET /_search/scroll
{
"scroll": "1m",
"scroll_id" : "DXF1ZXJ5QW5kRmV0Y2gBAAAAAAAAQoAWOWV3WnBTMVNSNFc1ZXAzeGxlWGJ1UQ=="
}
返回结果
{
"_scroll_id" : "DXF1ZXJ5QW5kRmV0Y2gBAAAAAAAAQoAWOWV3WnBTMVNSNFc1ZXAzeGxlWGJ1UQ==",
"took" : 1,
"timed_out" : false,
"terminated_early" : true,
"_shards" : {
"total" : 1,
"successful" : 1,
"skipped" : 0,
"failed" : 0
},
"hits" : {
"total" : {
"value" : 3,
"relation" : "eq"
},
"max_score" : 1.0,
"hits" : [
{
"_index" : "product",
"_type" : "_doc",
"_id" : "2",
"_score" : 1.0,
"_source" : {
"name" : "huawei shouji",
"desc" : "4G 5G",
"tags" : [
"shouji"
]
}
}
]
}
}
可以发现这个数据是id=2的。以此类推,拿着id=2的scroll_id去查会查到id=3的。但是这期间有新的数据插入进来了,是不会被查到的,因为查的是快照。
3.3、补充说明
- 指定的1m代表的是持续滚动时间,如果过了1分钟,还没有查询下一页,那么这个scroll_id就会被es清理掉。就无法用这个scroll_id去查数据。还可以指定1s,代表1秒钟。
- 删除scroll_id
DELETE /_search/scroll/DXF1ZXJ5QW5kRmV0Y2gBAAAAAAAAQoAWOWV3WnBTMVNSNFc1ZXAzeGxlWGJ1UQ==
- 删除所有scroll_id
DELETE /_search/scroll/_all
四、优化
- 当你的数据分页超过5000的时候,不用用deep paging
也就是比如1w条数据,假设每页10条,1000页。这时候第5000条数据在第500页,正常人你去百度搜索,你也就点前几页就完了,谁会点到几百页。但是不能排除这种情况,所以建议前N页采取
from/size
,在之后就采取scroll,但是scroll不支持上一页的操作,看产品需求。
- 返回结果500以下为最佳
避免每页返回几千条数据。