TiDB 6.5 LTS 版别现已发布了。这是 TiDB V6 的第二个长期支撑版,携带了许多备受等候的新特性:产品易用性进一步提高、内核不断打磨,更加成熟、多样化的灾备能力、加强运用开发者生态构建……

TiDB 6.5 新特性解析系列文章由 PingCAP 产研团队重磅打造,从原理剖析、技术完成、和产品体会几个层面展现了 6.5 版别的多项功用优化,旨在协助读者更简略、更全面的体会 6.5 版别

本文为系列文章的第一篇,介绍了 TiFlash 在高并发场景下的稳定性和资源运用率的优化原理。

缘起

最近的某天,咱们测试 TiFlash 在高并发查询场景下的稳定性时,发现 TiFlash 总算能够长时刻稳定将 CPU 完全打满,这意味着咱们能充分的运用 CPU 资源。回想一年多前,咱们还在为高并发查询下的 OOM(out-of memory)、OOT(out-of thread)、CPU 运用率不高级各种问题而绞尽脑汁。是时分来回顾一下咱们做了哪些事情,让量变引起突变。

TiDB 6.5 新特性解析丨过去一年,我们是如何让 TiFlash 高效又稳定地榨干 CPU?

CPU 运用率打满

咱们都知道,关于剖析型的查询来说,有时分一个恳求就能将机器的 CPU 资源打满。所以,大部分 OLAP 体系在设计和完成的时分,很少考虑体系在高查询并发下的体现——前期的 TiFlash 也没在这方面考虑太多。

前期的 TiFlash 的资源办理比较初级——没有高效的线程办理机制、短少强健的查询使命调度器、每个查询的内存运用没有任何约束、最新写入的数据存储和办理也有较大优化空间。这些优化措施的缺位,导致前期的 TiFlash 在高并发查询场景下体现欠安,经常无法将 CPU 运用率打满,稍有不慎还可能呈现 OOM 或 OOT。

曩昔一年里,针对 TiFlash 在高并发场景下的稳定性和资源运用率这个问题,咱们在存储和计算上都做了不少尝试和尽力。如今回头看,有了上面的成果,也算是达到了一个小里程碑。

DynamicThreadPool

TiFlash 最开始的线程办理十分简略粗犷:恳求到来时,按需创立新线程;恳求完毕之后,主动毁掉线程。在这种形式下,咱们发现:关于一些逻辑比较复杂,但是数据量不大的查询,无论怎样添加查询的并发,TiFlash 的整机 CPU 运用率都远远不能打满。

TiDB 6.5 新特性解析丨过去一年,我们是如何让 TiFlash 高效又稳定地榨干 CPU?

CPU 运用率始终保持在 75% 以下

经过一系列的研究之后,咱们总算定位到问题的根本原因:高并发下,TiFlash 会频频地创立线程和开释线程。在 Linux 内核中,线程在创立和开释的时分,都会抢同一把大局互斥锁,然后在高并发线程创立和开释时, 这些线程会产生排队、堵塞的现象,进而导致运用的计算作业也被堵塞,而且并发越多,这个问题越严峻,所以 CPU 运用率不会跟着并发添加而添加。具体剖析能够参考文章:深入解析 TiFlash丨多并发下线程创立、开释的堵塞问题。

处理这个问题的直接思路是运用线程池,削减线程创立和开释的频率。但是,咱们目前的查询使命运用线程的形式是非抢占的,关于固定巨细的线程池,因为体系中没有大局的调度器,会有死锁的风险。为此,咱们引入了 DynamicThreadPool 这一特性。

在 DynamicThreadPool 中,线程分为两类:

  1. 固定线程:固定数量的线程,生命期与整个线程池相同。
  2. 动态线程:运转过程中跟着负载升高而创立,会自行在冷却后毁掉。

每当有新使命需求执行时,DynamicThreadPool 会按以下次序查找可用线程:

  1. 闲暇的固定线程。
  2. 闲暇的动态线程。
  3. 当没有可用线程时,创立新的动态线程服务当时使命。

一切闲暇的动态线程会组成一个 LIFO 的链表,每个动态线程在处理完一个使命后都会将本身刺进到链表头部,这样下次调度时会被优先运用,然后达到尽可能复用最近运用过的动态线程的意图。链表尾部的动态线程会在超越一个时刻阈值没有收到新使命之后判断本身已冷却,自行退出。

MinTSOScheduler

因为 DynamicThreadPool 没有约束线程的数量,在遇到高并发查询时,TiFlash 依然有可能会遇到无法分配出线程(OOT)的问题。为了处理此问题,咱们必须操控 TiFlash 中同时运用的线程数量。

为了操控同时运用的计算线程数量,同时避免死锁,咱们为 TiFlash 引入了名为 MinTSOScheduler 的查询使命调度器——一个完全分布式的调度器,它仅依赖 TiFlash 节点本身的信息。

TiDB 6.5 新特性解析丨过去一年,我们是如何让 TiFlash 高效又稳定地榨干 CPU?

MinTSOScheduler 的基本原理

MinTSOScheduler 的基本原理是:确保 TiFlash 节点上最小的 start_ts 对应的一切 MPPTask 能正常运转。因为大局最小的 start_ts 在各个节点上必然也是最小的 start_ts,所以 MinTSOScheduer 能够确保大局至少有一个查询能顺畅运转然后确保整个体系不会有死锁。而关于非最小 start_ts 的 MPPTask,则根据当时体系的线程运用状况来决议是否能够运转,所以也能达到操控体系线程运用量的意图。

MemoryTracker

DynamicThreadPool 和 MinTSOScheduler 基本上处理了线程频频创立和毁掉、线程运用数量不受操控两大问题。关于一个运转高并发查询的环境,还有一个重要的问题要处理——削减查询之间的彼此干扰。

实践中,咱们发现最重要的一点就是要避免其中某一个查询遽然消耗掉很多内存,导致整个节点 OOM。为了避免某个大查询导致的 OOM,咱们明显增强了 MemoryTracker 盯梢和记载每个 MPPTask 运用的内存的精确度。当内存运用超越约束时,能够强行间断恳求,避免 OOM 影响其它恳求。

PageStorage

PageStorage 是 TiFlash 中的一个存储的抽象层,相似目标存储。它主要是为了存储一些较小的数据块,如最新数据和 TiFlash 存储引擎的元数据。所以,PageStorage 主要面向新写入数据的高频读写设计。v6.1 及之前 TiFlash 运用的是 PageStorage 的 v2 版别(简称 PSv2)。

经过一系列的迭代和业务打磨,咱们发现 PSv2 存在一些问题亟需改善:

  1. 在一些写入负载,特别是 append-only 负载下,容易触发急进的 GC 战略对硬盘数据进行重写。重写数据时引起较大的写扩大,以及内存的周期性快速上涨,形成体系不稳定。同时也会抢占前台写入和查询线程 CPU。
  2. 在 snapshot 开释时进行内存中的废物回收,其中触及较多内存小目标的拷贝。在高并发写入和查询的场景下,snapshot 开释的过程与读写使命抢占 CPU。

这些问题在大部分写入和查询并发较低的 OLAP 场景下,对体系的影响有限。但是,TiFlash 作为 TiDB 的 HTAP 架构中重要的一环,经常需求面对高并发的写入和查询。为此,咱们重新设计并完成了全新的 PageStorage (简称 PSv3)以应对更苛刻的 HTAP 负载需求。

TiDB 6.5 新特性解析丨过去一年,我们是如何让 TiFlash 高效又稳定地榨干 CPU?

PSv3 架构图

上图是 PSv3 的全体架构。其中,橙色块代表接口,绿色块代表在硬盘上存储的组件,蓝色块代表在内存中的组件。

  • WALStore 中保护数据(page)在 BlobFile 中位置,内存中的 PageDirectory 完成了 MVCC 支撑。
  • 数据保存在 BlobFile 中,假如其中的数据反复重写,会形成 CPU 以及 IO 资源的浪费。咱们经过 SpaceMap 记载了 BlobFile 上的数据块运用状况(闲暇或占用)。删去数据时,只需求更新一下标记。写入数据时,能够直接从 SpaceMap 查找符合要求的闲暇块直接写入。大部分状况下,BlobFile 能够直接复用被删去的闲暇数据块,避免数据重写的产生,最大程度地削减了废物回收的需求,然后明显削减 CPU 和内存空间运用。
  • 因为有 SpaceMap 的存在,写线程在 SpaceMap 中分配好数据块位置之后,多个写线程的 IO 操作能够并发执行。在复用空间时 BlobFile 文件巨细不变,能够削减了文件元数据的 sync 操作。TiFlash 全体的写延迟下降,进而削减等候数据一致性的 wait index 堵塞时刻,提高查询线程的 CPU 运用率。
  • 让读写线程 snapshot 创立和开释时的操作更高效,内存目标的整理的时刻从开释 snapshot 时改为在后台线程进行回收,削减了对前台读写使命的影响,然后提高了查询线程的 CPU 运用率。

总结

DynamicThreadPool MinTSOScheduler PageStorageV3 CPU 最大运用率
enable enable enable 100%
disable disable enbale 75%
enable disable enable 90%
enable enable disable 75%
disable enable enable 85%

上面这个表格总结了本文介绍的这几个提高 TiFlash 稳定性和 CPU 运用率的要害特性的组合状况,能够看出:

  • DynamicThreadPool 处理了频频创立和毁掉线程带来的开支;PageStorage v3 大大下降了 GC 和 snapshot 的开支,提高了高并发写入和查询的稳定性。这两者对提高 CPU 运用率有明显的作用。
  • MinTSOScheduler 调度器约束了查询运用线程的数量,避免了呈现分配不出线程的状况,能够有用避免高并发恳求导致的 OOM、OOT。
  • 而 MemoryTracker(内存约束)经过主动 cancel 掉部分恳求来避免整个进程 OOM,能够有用避免一个大查询导致整个节点不可用(OOM)的状况产生。

除此之外,曩昔一年,TiFlash 在性能和功用方面也做了不少优化,感兴趣的朋友能够重视咱们的 github 代码和官方文档。以上全部改动能够在 TiDB v6.5 LTS 版别中体会到,欢迎尝试。