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被卡住,一直没有结束导致查询超时。

相关推荐
Mr_Air_Boy1 小时前
SpringBoot使用dynamic配置多数据源时使用@Transactional事务在非primary的数据源上遇到的问题
java·spring boot·后端
豆沙沙包?1 小时前
2025年- H77-Lc185--45.跳跃游戏II(贪心)--Java版
java·开发语言·游戏
mazhafener1231 小时前
智慧照明:集中控制器、单双灯控制器与智慧灯杆网关的高效协同
大数据
打码人的日常分享1 小时前
物联网智慧医院建设方案(PPT)
大数据·物联网·架构·流程图·智慧城市·制造
年老体衰按不动键盘2 小时前
快速部署和启动Vue3项目
java·javascript·vue
咖啡啡不加糖2 小时前
Redis大key产生、排查与优化实践
java·数据库·redis·后端·缓存
liuyang-neu2 小时前
java内存模型JMM
java·开发语言
UFIT2 小时前
NoSQL之redis哨兵
java·前端·算法
刘 大 望2 小时前
数据库-联合查询(内连接外连接),子查询,合并查询
java·数据库·sql·mysql
怀旧,2 小时前
【数据结构】6. 时间与空间复杂度
java·数据结构·算法