博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
一次数据库与es程序优化的过程
阅读量:4296 次
发布时间:2019-05-27

本文共 2551 字,大约阅读时间需要 8 分钟。

关键词: 桶数限制, 分区partition, cardinality, script

最近生产上的定时任务发生了OOM异常.很遗憾的是,st环境无法重现,生产环境也由于权限的问题无法定位,所以找不到问题的原因.

不过从程序来看,代码还是有很大的优化空间.

目前业务现状(模拟的业务场景):

1,表中有约140万条不同的会员卡(卡信息表)数据,对应有大约3200万条消费记录存在es中.

2.需要对半个月/一个月都未消费的会员卡发送短信提醒,如果卡有异常(另一表记录),则不发送

3.同一张会员卡可以给多个人使用(会员与卡映射表)

现有程序设计:

1.分批遍历卡信息,剔除有异常的会员卡

2.用每张卡去ES查询最后一条消费记录的时间,然后判断是否半个月/一个月都未消费

3.符合条件的卡发短信提醒卡的所有成员

缺点:

1.需要全部卡都进行遍历,并到ES进行聚合查询,再做判断,占内存多,计算量大,耗时

2.查卡过程的SQL使用了not exist,查询时间长

3.很多会员卡不符合条件,做了不必要的筛选,耗时且浪费巨大

因为无法找到内存溢出的原因,只有通过其他方法进行代码优化.


=>改进办法:

1.设计思路: 原先是 会员卡->es查询最后一笔消费时间->符合条件的数据->发短信 (正向)

改为:es分别获取最后一笔交易是半个月前,一个月前的会员卡->数据库查会员卡信息->发短信 (反向)

2.发短信步骤改为异步

为什么原先的设计没有按这种思路来呢? 是因为当时一直找到符合要求的es脚本去筛选数据.所以只能遍历会员卡,而不是相反先从es筛选开始. 光是这一步就有巨大的区别.


参考资料:

在es使用聚合查询时,由于,因此需要对查询进行分区(即分批次循环查询)

先获取es总记录数

private int getRecordTotalNum(RestHihLevelclient client) {    SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();    CardinalityAgeregationBuilder countAee = AggregationBuilders.cardinality(CARD_CNT).field(CARD_ID);    sourceBuilder.agregation(countAgg.size(0);    SearchRequest request = new SearchRequest(INDEX_NAME).types(TYPE_NAME).source (sourceBuilder);    long total = 0;    try {        SearchResponse search = client.search(request);        Cardinality cntAgg = sesearch.getAggregations().get(CARD_CNT);        total = cntAgg.getValue();        catch (Exception e) {        log.error("查询ES异常", e);    }    return total;}

至于每个批次数据量多少,要根据实际情况而定,如综合查询所需时间,cup消耗等.

在分区的聚合查询中,要对聚合结果进行having过滤(筛选消费时间为半个月前).

而此时又发现新的问题:查询结果会比实际的时间少8小时--原来是时区原因导致的.

所以格式化要设置时区, es脚本大致如下:

{	"size": 0,	"aggregations": {		"by_card_id": {			"terms": {				"field": "card_id",				"size": 1000000,				"min_doc_count": 1,                    .                    .                    .				"include": {					"partition": 0,					"num_partitions": 12				}			},			"aggregations": {				"by_trx_tim": {					"max": {						"field": "trx_tim"					}				},				"having": {					"buckket_selector": {						"buckets_path": {							"trxTim": "by_trx_tim"						},						"script": {							"source": "SimpleDateFormat sdf = new SimpleDateFormat(\"yyyy-MM-dd\");										sdf.setTimeZone(TimeZone.getTimeZone(\"GMT0\"));										String tt = sdf.format(params.trxTim);										return tt.compareTo(\"2020-08-26\") == 0;",							"lang": "painless"						},						"gap_policy": "skip"					}				}			}		}	}}

由此获取会员卡卡号,就是满足半个月无消费的条件的数据了. 再做一次满足一个月的查询,就得到全部要发短信的会员卡.

此时再单独查出消费异常的会员卡,剔除掉即可,不用和以前那样做not exist耗时的操作了.

剩下的就是组装信息发短信了.

总结:

原先对ES了解很浅,并不知道ES有类似having的查询,也不知道可以插入自定义查询脚本. 当时也试图找过相关的资料,可能是查资料不得法,没有找到这块内容.所以问题驱动开发,能不断扩充知识.

有疑问或不同的观点,欢迎提出,一起探讨.

 

 

转载地址:http://rhbws.baihongyu.com/

你可能感兴趣的文章
聚合搜索引擎
查看>>
Python super钻石继承
查看>>
回测引擎代码分析流程图
查看>>
Excel 如何制作时间轴
查看>>
股票网格交易策略
查看>>
matplotlib绘图跳过时间段的处理方案
查看>>
vnpy学习_04回测评价指标的缺陷
查看>>
ubuntu终端一次多条命令方法和区别
查看>>
python之偏函数
查看>>
vnpy学习_06回测结果可视化改进
查看>>
读书笔记_量化交易如何建立自己的算法交易01
查看>>
设计模式03_工厂
查看>>
设计模式04_抽象工厂
查看>>
设计模式05_单例
查看>>
设计模式06_原型
查看>>
设计模式07_建造者
查看>>
设计模式08_适配器
查看>>
设计模式09_代理模式
查看>>
设计模式10_桥接
查看>>
设计模式11_装饰器
查看>>