最近遇到一个性能问题,并发场景下查询性能会有明显下降,根据日志发现同一个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被卡住,一直没有结束导致查询超时。
