场景:用户每次操作,需要会有一次记录入库,现在需要各种聚合计算查询。
难点:mysql内存、CPU计算能力有限,故而考虑引入ElasticSearch。
结果:将这个查询当做服务,使用dubbo开放给业务方调用。
实现场景很简单
但是问题出来了,es-statistics服务挂了,这时候业务日志就丢失了。
有些日志是可以丢失,有些敏感日志不能丢失,假如业务的是用户打款日志的,就要高度依赖这个log的数据。
于是这里引入了一个log表:
仔细思考下,这个表应该放在哪里?es-statistics服务库还是业务库?
两者各有利弊!
如果放在业务库,优点很明显,服务如果挂了,数据不会丢失。缺点也明显,这个表可能我业务库用不到,多出来的一块业务存储;
如果放在es-statistics服务库,优点很明显,对业务的0入侵,缺点也明显,服务挂了,数据还是会丢失;
这里我提供了下我的想法,按照目前的场景,我更倾向于放在es-statistics服务库。
1.如果放在业务库,这时候服务挂了,数据虽然还在,但是业务方需要自己同步数据重新写入es-statistics服务,需要考虑从什么时候开始写,如何避免重复写入,业务调用方怎么区分唯一性?
2.如果该服务对接了多个业务调用方,那每个业务调用方都需要考虑这些事情。
3.可以提高服务的可用度
4.尽量减少服务端的发布
所以针对第3点和第4点,我们继续对这个服务做优化。
现在的情况是,只是打款的一个操作日志,假如明天产品说,也需要对退款做一次统计报表,你这个log表是不是就嘻嘻了?这时候就技术哥哥再加一张log4refund表,后天产品又说,我要对差评的做一次统计表,小哥又加一张log4rate表?大后天呢?需求是无穷无尽的。而且每次加一张表,就需要发布重启一次。
所以,我们重新设计了log表,将log尽量做成通用化,所以的操作内容,序列化为json,统一用一个大字段存储。
这时候问题由来了,虽然log表兼容了各种业务类型,但是插入log表以后再存储到ES的地方还是要区分各个业务的数据的。每次还是需要发布重启。
既然如此,何不把插入数据库的逻辑和插入ES的逻辑拆分出来?
提高服务的可用性,排除网络和机器等硬性限制,我们尽量使服务越简单越好,最好就一行代码。这个服务只做一件事情,就是插入数据库。我们重新开一个任务,跑这个log表的数据,使用工厂模式,不同的业务type类型,实例化不同的对象,插入到不同索引的ES中。
至于如何跑这个es-task,我们可以在log表中再加入一个created字段,根据时间增量的跑这个数据。
到这里,基本上这个设计差不多结束了,insert(log)服务越简单越好,不用依赖其他框架,不必每次都重启,服务线上同时部署多个点的话,基本可以不用管。后面再设想一步,甚至我可以把insert(log)服务和log表都可以去掉,在业务方和es-statistics直接开通一个消息队列,业务方插入消息,es-statistics接受消息转化自己的数据。
或者不使用消息队列,那就业务方将insert(log)服务和自己业务当做一个事务,如果insert(log)调用失败,回滚自身业务数据,当然这里也要考虑假如业务调用方调用服务的结果是超时了,实际上insert(log)是执行成功的,这就需要考了一个唯一键,具体的优化交给大家慢慢思考。
最后,es-task怎样能快速的将log表的数据消化了,瓶颈可能是单个点跑,速度提升不起来,这里就需要一套分布式任务调度系统,下次我再抽时间整理下整个逻辑。