解密TaurusDB存储端高并发之线程池

45次阅读

共计 2169 个字符,预计需要花费 6 分钟才能阅读完成。

摘要:为了能加快相关任务的高效执行,TaurusDB 采用多线程技术处理的方式,增加处理器单元的吞吐能力,从而提高存储端的执行效率。

1. TaurusDB 背景

随着云计算进入 2.0 时代,数据急剧膨胀,这对实现数据库的高可靠、高性能、高吞吐的目标产生了巨大的挑战。如图 1 所示,TaurusDB 是华为自研的最新一代企业级具备横向扩展、海量存储能力的分布式数据库,其采用了计算存储分离,一写多读的分布式架构。将原本计算层的高密度存储相关压力下沉到存储层,极大地释放了计算层的算力。但同时将原来的存储 IO 转移到了网络 IO,这也就是意味着,存储层将面临来自计算层风暴级的压力。如果存储层不能快速响应计算层的读写请求,会极大影响用户的使用体验。

图 1 TaurusDB 整体架构

图 2 slice 功能组件

从图 2 可知,TaurusDB 的存储层,不单单只做存储相关的工作,也需要大量的算力,比如 consolidation 生成特定数据页、compation 回收旧版本数据、BufferPool 缓存热点数据页等任务。为了能加快这些任务的高效执行,我们首先能想到的就是能够并行执行这些任务,也就是采用多线程技术处理的方式,增加处理器单元的吞吐能力,从而提高存储端的执行效率。

2. 线程池化设计思想

2.1 线程为什么需要池化

首先,线程是稀缺的资源,如果频繁创建和销毁线程的开销是可观的,所占用的时间可能多于实际任务的执行;且当需要执行任务时,都去创建一个对应的线程去处理,那么服务器的资源 (比如地址空间和内核参数) 很快就会被耗尽,导致而导致 OOM 问题。

其次,通过事先创建好一定数量的线程并置于公共池之中,这样当有任务需要执行时,只需从公共池取一个线程执行当前的任务即可,待任务结束后,此线程又可以执行其他任务或处于休眠状态,等待下一次被调度,达到线程资源重复使用的目的。

2.2 线程池如何管理

为了能有效的管理多线程,TaurusDB 存储端采用了如图 3 的线程池模型。

图 3 线程池模型

ThreadPool: 主要负责控制线程池的大小、状态变更、线程的创建、销毁、调度策略的选取;

Scheduler:负责具体任务的接收、被调度的顺序,并触发任务的执行;

Worker:负责具体任务的执行;

Monitor: 负责监控线程执行任务时是否出现异常,以及异常告警,比如线程执行一次任务长时间执行未能结束。

2.3 线程池的调度策略

当前 TaurusDB 存储端线程池支持三种策略:先进先出调度(FifoScheduler)、定时调度(TimeScheduler)、基于容量调度(CapacityScheduler)。

对于 FifoScheduler 和 TimeScheduler,比较容易理解。当有任务需要执行时,只需将此任务存放在一个队列即可,有 scheduler 按照顺序逐一调度即可。

对于 CapacityScheduler,是一种基于事先为某一类型的任务预留可执行线程的思想,其预留的线程个数由下发任务的用户指定。具体调度过程见图 4。

图 4 CapacityScheduler 调度

比如:

初始化线程大小为 10,TaskType1 预留线程数 4,TaskType2:预留线程数 5,TaskTypeN:预留线程数 4

当线程池处于如图 4 状态时,任务类型是 1 和 3 的尚未达到预留值,任务类型 N 已达到阈值。此时如果 Threadpool 中处于 idle 的线程数为 1,则该线程将会被调度到任务类型为 2 的队列中。

2.4 任务异常监控告警

我们知道,一旦任务被调度的线程执行过程中,可能会出现异常情况,比如线程死锁,导致该任务不能按照预期推进,轻者引发系统出现 CPU、IO 等系统资源使用率飚高的情况,严重者会导致系统 down 情形。比如 TaurusDB 的存储端,执行 log 的 checkpoint 的线程出现长时间卡顿,会导致存储端旧的 log 不能正常回收,导致磁盘空间逐步膨胀,进而影响存储端其他各个模块平滑的推进。如果能够识别出处于异常状态的线程,并能够进行告警,基于事先自定义规则进行修复,将能够持续保证存储端业务的连续性。

线程池 Monitor 组件就是用于识别处于异常状态的线程,其基本思想就是,定期巡检线程池中的各线程处于状态,如果发现线程状态长时间未更新,则判定该线程处于异常状态,上报告警,并基于相应的处理规则处理。

3. 下一步演进方向

从 2.3 中可以看出,CapacityScheduler 策略是基于实际在执行的线程数,作为 idle 线程线程被调度的依据,尚未衡量实时任务的重要程度。考虑这样一种场景,如果有两种类型的任务 A、B,在某一时间,用于执行 A、B 任务的线程数恰好线程或差值极小,但 B 类型任务的优先级大于 A 任务,这时可能出现 idle 线程被调度执行 A 类型任务,B 类型任务不能分配到充足的线程数(预留值是静态分配),用于加快推进任务的处理。

考虑的方案:

1. 限制类型任务类型队列的长度,这样可以均衡各类型任务的调度,不至于某一类或几类任务数过多;

2. 在系统资源有限的前提下,支持动态伸缩线程池大小的功能,这样可以在工作负载过重时,扩充线程池大小,用于调度到急需执行的任务;

3.099 进一步细化 CapacityScheduler 策略,采用任务的重要程度和实际执行的线程数的规则,作为 idle 线程被调度的依据。

[点击关注,第一时间了解华为云新鲜技术~]

正文完
 0