业务自增id彻底解决es深翻页的问题
背景
由于原来mysql库里面的数据量从2万增加到40万,并且mysql库缩容导致,每小时其他项目级小伙伴来调xinyun库的这张表导致cpu每次开销都在20%以上,并且该库数据可能还会继续增加,每次拉数据会延长到半小时左右,非常耗能,所以将原有的mysql表同步到es中...,但是发现es翻页到一万条数据后就会出现以下问题。
通过查询发现该问题在es使用中普遍存在,有三种方案:
一、 max_result_window 的值调至 50000。
[root@dnsserver ~]# curl -XPUT "127.0.0.1:9200/custm/_settings" -d
'{
"index" : {
"max_result_window" : 50000
}
}'
二、通过scroll分页通过游标来获取;
三、通过scroll after 来通过上下页;
通过业务的分析和尝试,发现以上的三种方案都行不通....
方案一,这个可能会影响整个集群,不能随便改;
方案二和三,因为原有的接口传过来一个页码,原接口现在暂时不支持修改,所以如果要改可能要让原来下游接换接口,并且无法返回游标过来,这个比较头大。
尝试过的方案:
将游标放到缓存中-------->存在一定风险,因为页数是一页一页翻页,如果哪天来一个突然间100页的,不支指定页码,并且还有可能存在死循环调用风险。
通过传入的页面,循环获取游标不断提交,这个时间太长了代码和时间如下:
通过指定循环到指定页码获取,发现前十页还好,后面就...
6秒多.....有点恐怖。
然后不断的寻找问题的解决方案中,不断尝试...。在同事的分享下,彻底解决了该问题,真的非常给力;
解决方案:
通过原数据库自增Id将通过条件方式进行区间条件查询,每次分页请求的时候,通过页码*条数获取指定范围的条数就解决了该问题。
代码实现
@Override
public DataRO queryByCondition(PopFlowConfigQueryAo queryAo) {
LOGGER.info("查询参数:{}", JSONObject.toJSONString(queryAo));
SearchRequestBuilder searchRequestBuilder = buildSearchRequestBuilder();
BoolQueryBuilder boolQueryBuilder = boolQuery();
if(null!=queryAo && queryAo.getPageAO()!=null && queryAo.getPageAO().getPageIndex() != 0 && queryAo.getPageAO().getPageSize()!= 0){
int page = (queryAo.getPageAO().getPageIndex()-1);
// searchRequestBuilder.setSize(queryAo.getPageAO().getPageSize());
RangeQueryBuilder rangeCreate = QueryBuilders.rangeQuery("popFlowConfigId");
int index = page * queryAo.getPageAO().getPageSize();
rangeCreate.gte(index);
int size = ((1+page)* queryAo.getPageAO().getPageSize())-1;
rangeCreate.lt(size);
boolQueryBuilder.must(rangeCreate);
}else{
searchRequestBuilder.setSize(10000);
}
searchRequestBuilder.setQuery(boolQueryBuilder);
searchRequestBuilder.addSort("popFlowConfigId", SortOrder.ASC);
LOGGER.info("pop查询{}", searchRequestBuilder.toString());
SearchResponse searchResponse = searchRequestBuilder.get();
DataRO> response = new DataRO<>();
List resultList = new ArrayList<>();
SearchHits hits=searchResponse.getHits();
int count = (int) searchResponse.getHits().totalHits;
LOGGER.info("查询总条数{}",count);
List list = BeanUtil.transformListBean(hits, PopFlowConfigRo.class);
if (list != null && list.size() > 0) {
resultList.addAll(list);
}
response.setData(resultList);
return response;
}
通过排序popFlowConfigId为正序;
再通过原数据库主键:popFlowConfigId 进行区间的条件查询,比如
传入页码为1的时候通过代码:
默认减1当成0,由于es也是通过区间rangeQuery
popFlowConfigId>= 0 * size = 0;
popFlowConfigId<((1+page) * 1000)-1;
通过这个算出:
page = 1
form -> 0 ,size -> 999
range 0 ~ 999
page = 2
form -> 0 ,size -> 999
range 1000 ~ 1999
....
得出 from=0 size=999 这里就类似数据库的 0~999区间,这样的话也就是说,每只查出这个popFlowConfigId在这个区间中的数据,一页一页的查,并且解决了由于scroll 或scroll after无法指定页码查询问题或需要通过游标,而且性能也非常高。
优化后
- 面向对象系列讲解—面向对象的含义&工厂模式
- 庖丁解牛看委托和事件(续)
- ReactiveCocoa中潜在的内存泄漏及解决方案
- 面向对象系列讲解——混合模式
- 火力全开——仿造Baidu简单实现基于Lucene.net的全文检索的功能
- Go实战--实现简单的restful api
- 特殊字体神器-fontmin,秒杀一切工具
- 庖丁解牛——深入解析委托和事件
- RestQL:现代化的 API 开发方式
- 在递归函数中因不正确使用公共变量而形成死循环
- 用R语言做时间序列分析(附数据集和源码)
- Windows Live Writer插入代码vs2010插件
- 分布式队列编程优化篇
- 基于机器学习方法的POI品类推荐算法
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法