tidb6.5新特性解析丨过去一年,我们是如何让tiflash高效又稳定地榨干cpu?

TiDB_PingCAP TiDB_PingCAP     2023-03-29     233

关键词:

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 使用率不高等各种问题而绞尽脑汁。是时候来回顾一下我们做了哪些事情,让量变引起质变。

👆 CPU 使用率打满

我们都知道,对于分析型的查询来说,有时候一个请求就能将机器的 CPU 资源打满。所以,大部分 OLAP 系统在设计和实现的时候,很少考虑系统在高查询并发下的表现——早期的 TiFlash 也没在这方面考虑太多。

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

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

DynamicThreadPool

TiFlash 最开始的线程管理非常简单粗暴:请求到来时,按需创建新线程;请求结束之后,自动销毁线程。在这种模式下,我们发现:对于一些逻辑比较复杂,但是数据量不大的查询,无论怎么增加查询的并发,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 节点自身的信息。

👆 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 负载需求。

👆 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 利用率。

总结

DynamicThreadPoolMinTSOSchedulerPageStorageV3CPU 最大使用率
enableenableenable100%
disabledisableenbale75%
enabledisableenable90%
enableenabledisable75%
disableenableenable85%

上面这个表格总结了本文介绍的这几个提升 TiFlash 稳定性和 CPU 使用率的关键特性的组合情况,可以看出:

  • DynamicThreadPool 解决了频繁创建和销毁线程带来的开销;PageStorage v3 大大降低了 GC 和 snapshot 的开销,提升了高并发写入和查询的稳定性。这两者对提升 CPU 利用率有明显的效果。
  • MinTSOScheduler 调度器限制了查询使用线程的数量,避免了出现分配不出线程的情况,可以有效防止高并发请求导致的 OOM、OOT。
  • 而 MemoryTracker(内存限制)通过主动 cancel 掉部分请求来防止整个进程 OOM,可以有效避免一个大查询导致整个节点不可用(OOM)的情况发生。

除此之外,过去一年,TiFlash 在性能和功能方面也做了不少优化,感兴趣的朋友可以关注我们的 github 代码和官方文档。以上全部改动可以在 TiDB v6.5 LTS 版本中体验到,欢迎尝试。

json丨认识json是如何解析成类的(代码片段)

...常用的同学来说,使用Json就完全靠猜了:Json如何解析?Json如何生成?为什么有的数据可以转化成Json,有的数据转化后内容是空的?那我们 查看详情

最全的swift4新特性解析

...ft4也伴随着Xcode9测试版来到了我们的面前,很多强大的新特性非常值得我们期待在正式项目中去使用它。因为Swift4是开源的,如果你关注swift-evolution这个项目的话,就应该已经提前了解到它的新特性了。本文参考了WWDC2017以及各... 查看详情

坚如磐石:tidb基于时间点的恢复(pitr)特性优化之路丨6.5新特性解析

本文介绍了TiDB数据库的基于时间点的恢复(PiTR)特性,该特性允许用户将数据库恢复到特定时间点,从而避免丢失重要数据。文章首先介绍了PiTR技术的基本概念和工作原理,接着探讨了TiDB对PiTR的优化,... 查看详情

如何快速让浏览器兼容es6特性(代码片段)

...ceur,es6-shim等。此外,浏览器自身也加快速度兼容ES6的新特性,其中对ES6新特性最友好的是Chrome和Firefox浏览器。学习过程中我选择Babel作为学习编译e 查看详情

如何选择过去一年的过去 7 天及其对应的一周

】如何选择过去一年的过去7天及其对应的一周【英文标题】:HowcanIselectthepastsevendaysanditscorrespondingweekinthepastyear【发布时间】:2019-12-2120:12:33【问题描述】:如何选择过去一周前一年的对应天数这是仪表板所必需的,我想显示一... 查看详情

json丨认识json是如何解析成类的(代码片段)

...常用的同学来说,使用Json就完全靠猜了:Json如何解析?Json如何生成?为什么有的数据可以转化成Json,有的数据转化后内容是空的?那我们这个系列就从零开始,一步步学习Json的使用方法。一、Json跟U... 查看详情

flutter2.5的新特性(代码片段)

您好,欢迎来到Flutter2.5!这是一个大版本,在Flutter版本历史上排名第二:关闭了4600个问题,从252个贡献者和216个审阅者合并了3932个PR。如果我们回顾过去一年,我们会看到1337位贡献者创建了21,072个巨大... 查看详情

ecmascript2019(es10)新特性简介

...的第十个版本,所以也称为ES10.今天我们讲解一下ES10的新特性。ES10引入了2大特性和4个小的特性,我们接下来一一讲解。Array的新方法flat和flatMap在ES10中,给Array引入了两个新的方法,分别是flat和flatMap。先来看一下flat。我们看... 查看详情

揭秘lol背后的it基础架构丨产品而非服务

...)平台,这些平台使游戏的发行和运营变得更加轻松。在过去的两年前的一篇文章中,MaxfieldStewart介绍了有关开发生态系统,以及当时使用的许多工具。这里我们将更新一些最新的内容,包括面临的新挑战,如何解决问题,以及... 查看详情

jdk新特性(代码片段)

JDK1.5新特性JDK1.5的新特性:自动拆装箱,泛型,增强for,静态导入,可变参数,枚举枚举概述:就是一个类只能存在几个固定的对象,那么这个就是枚举.我们就可以使用这些对象可以表示一些固定的值.举例:一周只有7天,一年只有12个月... 查看详情

这一年我们上线的自动化系统

新年之初我就写了篇文章2022,房车使用这一年总结过去一年的日常生活,2022年除了认真的生活之外,在工作上也有了不小的变化,首先是2021年底从工作了近10年的公司离职,在2022年的第一个工作日就入职了新公司,算是无缝衔... 查看详情

apacheecharts5震撼发布:五大模块,十五项新特性全面升级!(代码片段)

数据可视化在过去的几年中得到了长足的发展。开发者对于可视化产品的期待不再是简单的图表创建工具,而在交互、性能、数据处理等方面有了更高级的需求。ApacheECharts始终致力于让开发者以更方便的方式创造灵活丰富的可... 查看详情

2022年将至,新一年的“赚钱战斗模式”你准备好了吗?

...D:hellobtc)临近年末,很多人都在复盘自己在过去一年里的投资经历。其中有不少人表示在2021年里踩准了NFT、链游和ENS等风口赚的盆满钵满,也有不少人感叹错失良机已拍断大腿。不过,无论过去如何,... 查看详情

飞凌干货丨如何选择电源适配器

...寿命等标准,还有一些是我们容易忽略的电源输出/输入特性。下面我给大家简单介绍一下这些技术规格以及飞凌在调试产品时所遇到的问题。以下提到的技术规格参考了国家标准GB/T14714-2008《微小型计算机系统设备用开关电源通... 查看详情

es6新特性

...ES6这个大兄弟加入我们的技术栈中。但是ES6那么多那么多特性,我们需要全部都掌握吗?秉着二八原则,掌握好常用的,有用的这个可以让我们快速起飞。接下来我们就聊聊ES6那些可爱的新特性吧。1.变量声明const和let我们都是... 查看详情

从redis7.0发布看redis的过去与未来

...本,它不仅包含了50多个新命令,还有大量核心新特性与改进,这些不仅能够解决用户使用中的诸多问题,还进一步拓展了Redis的使用场景。虽然Redis7.0做了许多大胆的尝试,但是稳定性依然是最重要的基石。Re... 查看详情

软件测试周刊(第56期):新年的目的并非是拥有新的一年,而是拥有一个新的灵魂。

...C今天是2022年01月28日,欢迎来到第56期!这里记录过去一周我们看到的软件测试及周边的行业动态,周五发布。本期看点:网易有道的UI自动化测试落地方案是什么?如何使用Rest-Assured进行接口自动化测试?... 查看详情

一文回顾.netcore基础设施演进之路

...多基础设施方面的挑战都源于存储库结构的隔离和分布式特性。尽管多年来它变化很大,但该产 查看详情