涉及技术简述
微服务架构,k8s部署管理,大数据导入数据库的功能涉及两个服务,一个负责csv文件的定时读取,一个负责文本的处理及入库。 读取服务会将文件内容按OLT粒度涉及的硬件消息,分为约5000条5~10kb的消息发送到kafka指定Topic的同一个分片,此项目分区pattern只设置了2个。均匀分发消息。
处理入库服务则是从消息队列读取消息。然后线程池并发处理。
踩坑及处理
我们的目的是,尽量快速处理约1.5GB的csv文件,具体到数据库的record约1000w左右,最开始预期是15分钟内处理完毕,以下是踩坑:
- 入库速度非常缓慢,约2个小时;
原因:消息队列消息提交偏移量的方法是手动提交,每条消息1执行,提交偏移量阻塞导致。 解决方案:最先考虑的是采用并发消费,但因为数据要求有顺序性,改为并发消费之后,整个功能就坏掉了。最后采用的是按整个OLT的消息,约5000条,进行一次偏移量提交 效果:入库由120分钟优化到60分钟。 - 数据库死锁频繁;
原因:间隙锁及where条件未能走索引导致行锁升级为表所 解决方案:排查了所有的SQL,where条件是有索引的,新增了where条件减小了行锁粒度,并更改了默认的数据库级别,由rr->rc。数据库死锁频率减小,但是仍然存在。 效果:60分钟到30分钟 - 线程池数据不安全;
原因:数据不安全 这个是剩余死锁问题的排查,查看数据库的最近死锁记录,发现两个不同表也会死锁,非常奇怪。 最开始发现消息队列消息似乎是重复消费了,但最后确认是因为线程池里的数据不安全导致的类似现象。 线程池提交的任务里,有调用一个函数,函数内部会指定一块内存地址保存处理后的数据,处理完毕后返回该地址给调用线程,但是问题就出现在这里,我的排查结果是,该函数返回的地址在多线程情况下,会导致线程不安全的问题。虽然改完之后死锁完全消失,但是最后也没能确认具体原理,待补充。 效果:速率没有更有效提升
后记
这个功能的优化持续了约10个工作日之久,期间涉及到的技术都是我之前没涉及的。在此列出 kafka:SpringBoot集成的kafkaTemplate、@Listener,分区pattern,并发消费,偏移量提交,kafka管理工具 线程池: 函数涉及的数据安全问题 数据库: 间隙锁,除select以外的命令未走索引导致的行锁与表锁,如何查看数据库死锁日志 k8s: kubectl的常用命令,docker命令,k8s基础概念
|