Trino是如何调度split的

最近遇到一个性能问题,并发场景下查询性能会有明显下降,根据日志发现同一个split,在不同线程切换之间会有很多时间损耗,比如线程1执行结束这个split, 到线程2开始执行这个split,中间有时会有2秒的空隙,导致这个split的耗时增加,进而导致整个查询的耗时飙升。

因此专门去学习了trino里面调度split的源码,看他是如何运作的。

TaskExecutor

TaskExecutor 里面有一个线程池,专门用来处理split, 每个线程执行一个TaskRunner,而TaskRunner的任务就是一个while循环,不停的尝试从waitingSplits队列里面获取split, 然后执行这个split。这个waitingSplits是一个类,叫做MultilevelSplitQueue,至于split是如何调度的核心逻辑就在这里面。

MultilevelSplitQueue

MultilevelSplitQueue中把所有split分为5个level, 5个level的阈值分别是下面定义的[0, 1, 10, 60, 300]。 含义就是说如果累计运行时间超过1s的任务就会被放到level1,以此类推。然后调用take方法获取split的时候,通过算法计算出目标level,然后从该level中选取优先级最高的split。

levelMinPriority是用来设置不同level之间执行时间分配的,默认是2,意味着level0~level4的时间占比为16:8:4:2:1。levelScheduledTime用来保存每个level的总耗时

take过程

take的核心逻辑如下图,在pollSplit方法中会遍历所有的level,然后根据每个level的总耗时计算出一个比例,选取比例最差的level做为目标level。接着会判断拿出来的这个split优先级是否有更新,如果split的优先级和对应的taskHandle的优先级对不上,那么就会把这个split放回队列再重新take。因为每次split调用process方法后,都会根据调度时间来更新level。

我们尝试打印了每次take时split的优先级,发现跑单个query时,所有的split都在level0, 而4个query并发跑时,就有挺多split在level1和level2,因为这些split的优先级低,被调用的几率更小,因此处理时间也会更长。

Split Run Quanta

这个参数代表每个split每次执行被分配的时间片,默认是1秒。在Driver的process方法中,有个while循环,会判断如果当前split的执行时间超过我们分配的时间,就会跳出循环。

执行结束

split执行结束后,会判断是否finish, 以及是否block, 如果还没结束且没有block, 则重新放入waitingSplits队列中,如果被block了,则需要加一个监听器,当block状态解除时,再放入等待队列。

Split Info

在每个split结束的时候,都会打印对应的split信息,包括这个split的开始时间,执行时间,cpu耗时以及调用了多少次processInternal。对于排查问题还是挺有帮助的,比如有一次查询偶发超时,就是通过这个日志定位到是某个split被卡住,一直没有结束导致查询超时。

相关推荐
人机1012 分钟前
Spring Security - FilterChainProxy
java
沧海一粟青草喂马18 分钟前
抖音批量上传视频怎么弄?抖音矩阵账号管理的专业指南
大数据·人工智能·矩阵
SimonKing33 分钟前
Apache Commons Math3 使用指南:强大的Java数学库
java·后端·程序员
渣哥34 分钟前
Java 集合迭代中的 fail-fast 与 fail-safe 机制详解
java
帧栈37 分钟前
我的创作纪念日
java
理智的煎蛋1 小时前
CentOS/Ubuntu安装显卡驱动与GPU压力测试
大数据·人工智能·ubuntu·centos·gpu算力
bug攻城狮1 小时前
Spring Boot Banner
java·spring boot·后端
赵孝正1 小时前
GitLab 分支管理与 Push 问题全解析
大数据·elasticsearch·gitlab
黑马源码库miui520862 小时前
JAVA同城打车小程序APP打车顺风车滴滴车跑腿源码微信小程序打车源码
java·微信·微信小程序·小程序·uni-app
MadPrinter2 小时前
SpringBoot学习日记 Day11:博客系统核心功能深度开发
java·spring boot·后端·学习·spring·mybatis