技术编程|基于Apache Doris的小米增长分析平台实践( 三 )


文章图片

对于Doris来说 , 一次stream load作业会产生一次事务 , Doris的fe进程的master节点会负责整个事务生命周期的管理 , 如果短时间内提交了太多的事务 , 则会对fe进程的master节点造成很大的压力 。对于每个单独的流式数据导入产品线作业来说 , 假设消息队列一共有m个分区 , 每批次的每个分区的数据导入可能执行最多n次stream load操作 , 于是对消息队列一个批次的数据的处理就可能会产生m*n次事务 。为了Doris的数据导入的稳定性 , 我们把Spark Streaming每批次数据的时间间隔根据业务数据量的大小和实时性要求调整为1min到3min不等 , 并尽量地加大每次stream load发送的数据量 。
在集群接入业务的初期 , 这套流式数据导入Doris的机制基本能平稳运行 。但是随着接入业务规模的增长 , 问题也随之而来 。首先 , 我们发现某些存了很多天数据的大表频繁地出现数据导入失败问题 , 具体表现为数据导入超时报错 。经过我们的排查 , 确定了导致数据导入超时的原因 , 由于我们使用stream load进行数据导入的时候 , 没有指定表的写入分区(这里线上的事件表都是按天进行分区) , 有的事件表已经保留了三个多月的数据 , 并且每天拥有600多个数据分片 , 加上每张表默认三副本保存数据 , 所以每次写入数据之前都需要打开约18万个writer , 导致在打开writer的过程中就已经超时 , 但是由于数据是实时导入 , 其他天的分区没有数据写入 , 所以并不需要打开writer 。定位到原因之后 , 我们做了相应的措施 , 一个是根据数据的日期情况 , 在数据导入的时候指定了写入分区 , 另一个措施是缩减了每天分区的数据分片数量 , 将分片数据量从600+降低到了200+(分片数量过多会影响数据导入和查询的效率) 。通过指定写入数据分区和限制分区的分片数量 , 大表也能流畅稳定地导入数据而不超时了 。
另一个困扰我们的问题就是需要实时导入数据的业务增多给fe的master节点带来了较大的压力 , 也影响了数据导入的效率 。每一次的 stream load操作 , coordinator be节点都需要多次和fe节点进行交互 , 如下图所示:
技术编程|基于Apache Doris的小米增长分析平台实践
文章图片

文章图片

曾经有段时间 , 我们发现master节点偶尔出现线程数飙升 , 随后cpu load升高, 最后进程挂掉重启的情况 。我们的查询并发并不是很高 , 所以不太可能是查询导致的 。但同时我们通过对max_running_txn_num_per_db参数的设置已经对数据导入在fe端做了限流 , 所以为何fe的master节点的线程数会飙升让我们感到比较奇怪 。经过查看日志发现 , be端有大量请求数据导入执行计划失败的日志 。我们的确限制住了单个db能够允许同时存在的最大事务数目 , 但是由于fe在计算执行计划的时候需要获取db的读锁 , 提交和完成事务需要获取db的写锁 , 一些长尾任务的出现导致了好多计算执行计划的任务都堵塞在获取db锁上边 , 这时候be客户端发现rpc请求超时了 , 于是立即重试 , fe端的thirft server需要启动新的线程来处理新的请求 , 但是之前的事务任务并没有取消 , 这时候积压的任务不断增多 , 最终导致了雪崩效应 。针对这种情况 , 我们对Doris主要做了以下的改造:
1. 在构造fe的thrift server的线程池时使用显式创建线程池的方式而非原生的newCachedThreadPool方式 , 对线程数做了相应的限制 , 避免因为线程数飙升而导致资源耗尽 , 同时添加了相应的监控 。
2. 当be对fe的rpc请求超时时 , 大部分情况下都是fe无法在指定时间内处理完请求导致的 , 所以在重试之前加上缓冲时间 , 避免fe端处理请求的堵塞情况进一步恶化 。
3. 重构了下GlobalTransactionMgr的代码 , 在保持兼容原有接口的基础上 , 支持db级别的事务隔离 , 尽量减少不同事务请求之间的相互影响 , 同时优化了部分事务处理逻辑 , 加快事务处理的效率 。


推荐阅读