Skip to content

Instantly share code, notes, and snippets.

@ywqzzy
Last active October 22, 2022 13:26
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ywqzzy/b525765462eae7d90a7e41831a6632b3 to your computer and use it in GitHub Desktop.
Save ywqzzy/b525765462eae7d90a7e41831a6632b3 to your computer and use it in GitHub Desktop.
TiDB Hackthon 2022 RFC----TiDB,YES!

TiDB,YES

队长: 严炜琦

队员:许张贤、付哲

项目地址: https://github.com/ywqdev/tiflash/tree/fiberpunkhackthon

项目介绍

FiberPunk

Fiber is all you need for TiFlash. 使用有栈协程 Boost::Fiber 构建协程池改造 TiFlash 线程执行模型,减少线程滥用,提升稳定性,增强查询性能。

动机

TiFlash 是一个实现了 MPP 查询的计算加速器。 TiFlash 执行器当前的多线程模型继承了早期版本的 Clickhouse,实现较为粗犷。 其线程模型如下: 针对于 MPP 查询中划分的每一个任务(MPPTask) 都会独立的创建一组线程来进行查询执行。利用 DynamicThreadPool 让 MPPTask 之间复用线程来降低线程创建和销毁的开销参考 深入解析 TiFlash丨多并发下线程创建、释放的阻塞问题)。 然而 DynamicThreadPool 不同于固定大小的线程池,在其线程资源不足时会额外分配线程以避免死锁问题。因此在高并发场景下 DynamicThreadPool 无法控制住系统计算所用的线程上限,线程将会毫无限制地进行分配。 在 TiFlash 现有的线程模型下,存在如下问题:

  1. 高并发场景存在严重问题。当线程数过多时将构造过多线程,导致查询失败。
  2. 在复杂查询下,存在阻塞算子(比如 join build),导致线程资源利用率不够高。

为什么 TiFlash 当前版本不使用线程池来控制线程资源

  1. TiFlash 使用 Volcano(pull based) 执行模型,在执行过程中存在大量阻塞,此时阻塞的任务将会占据线程池中线程资源,导致其他任务无法复用线程池内资源,线程资源利用率无法打满。
  2. 使用线程池可能导致 Task 的所有 job 同时运行,存在死锁风险。

作为 TiDB 的查询加速器,TiFlash 需要更高更快更强更稳定,其中查询执行器需要有效利用 OS 线程资源,榨干硬件所有性能,同时避免线程的过量使用以至于超越 OS 上限。

使用协程对 TiFlash 现有线程模型进行改造可以有效解决上述问题,仅需最小限度地修改 TiFlash 线程模型即可控制住 TiFlash 在高并发场景下的线程数。

协程的优势:

  1. 线程间的切换需要从用户态到内核态再回到用户态,这个过程会产生大量额外的开销,例如上下文切换,和内核的调度系统。我们可以通过选择使用用户态的线程来避免这部分的开销。
  2. 线程的频繁创建和销毁也会带来大量的开销,如果只是绑定固定数量的线程,并且通过协程合理使用它们,可以减小这部分开销。
  3. 使用协程能够将阻塞操作异步化,执行算子中的阻塞协程将被自动挂起,进入等待唤醒状态。而物理线程则开始调度下一个可执行协程。

我们将得到一个:

  1. 控制线程滥用,稳定性更强
  2. 性能更好

的 TiFlash 分支。

项目设计

技术选型

有栈协程or无栈协程?

有栈协程优势:

兼容性高,不需要给所有同步代码加上 async/await,改进现有线程模型时改动量小。

无栈协程优势:

相比于有栈协程,不需要把局部变量开辟在新开的空间上,直接使用系统栈,内存开销小,切换成本小。

考虑实现难度,且 AP 场景下线程切换较少,选择有栈协程库 Boost::fiber。

详细设计

利用 Boost 携带的协程库 Fiber 实现一个协程池 FiberPool。该协程池将申请固定个数的线程资源,确保运行时线程资源个数不变。 TiFlash 计算层中所有申请计算资源的任务均从 FiberPool 中申请资源。

MPPTask申请cpu资源

TiFlash 在算子间插入了一些算子来控制 Volcano 模型执行期不同阶段的并发,这些算子包括 UnionBlockInputStream, SharedQueryBlockInputStream, CreatingSetBlockInputStream, ParallelAggregatingBlockInputStream。 由于这些算子均需要申请线程进行执行,故而需要对其申请线程的流程进行修改,上述算子的功能分别如下:

  1. UnionBlockInputStream: 将多个 Streams 的输出结果汇聚到一个 stream 中,利用了 ParallelInputsProcessor 将结果放入 output_queue 中。
  2. SharedQueryBlockInputStream: 将一个 stream 输出到多个 streams 中。原有实现利用 ThreadManager 申请线程资源。
  3. CreatingSetBlockInputStream: 接受一个数据 InputStream 和代表 JoinBuild 的若干个 Subquery,并发启动这些 Subquery(利用 ThreadManager 申请线程资源),并等待它们执行结束之后再开始启动数据 InputStream。
  4. ParallelAggregatingBlockInputStream: 聚合,将多个 stream 并发进行聚合。 其中 partial 阶段将数据划分为 N 份并发进行 HashTable 的构建。Merge 阶段将 N 个 HashTable 合并,输出单个 stream。其中并发执行利用了 ParallelInputsProcessor。

其中 ParallelInputsProcessor 提供并发执行多个 BlockInputStream 的能力。

需要修改上述算子中的线程相关代码,包括线程池和并发元语,替换为 Fiber 相关代码,最终完成 Fiber 对原有线程模型的替换。

@zanmato1984
Copy link

牛啊!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment