本文共 2611 字,大约阅读时间需要 8 分钟。
维护操作(maintenance operations)作为本文阐述的对象,是在kudu正常运行的过程中,运行在后台的进程。为了保证kudu系统操作的流畅,维护管理器(MaintenanceManager)必须要明智的调度维护操作。某种程度上说,这是在当前性能和未来性能之间的权衡。比如说,做compaction操作就是消耗当前的IO,是的之后的插入性能提高。从另外一面说,这有是必须执行的任务,因为不执行的话会导致系统不稳定。比如,如果我们不去刷新MemRowSets,最终会导致kudu内存溢出。内存的的下降最终会导致处理请求速度的减慢。
为了做出正确的决定,我们需要权衡的最重要的事情包括以下几点:
除此之外,我们还考虑的其他一些标准(版本1中不需要考虑这些),包括:
如果管理得当的话,可用磁盘空间不应成为问题。我们后面还会讨论这点,但对于初始版本,最好假设我们有足够的空间。
我们现在不能考虑基于磁盘的调度,因为我们还没有支持多个磁盘。
内存使用可以分解为以下几个部分:
我们假设#1相对恒定。维护操作调度程序可以通过决定将某些MemRowSets刷新到磁盘来在#2和#3之间进行权衡。
我们希望保持#1,#2和#3所占的总内存量不会过大。目前,我们的目标是保持这总内存使用的相对稳定。我们还没有实现将tcmalloc保存的内存返回给操作系统。
如果我们知道tablet的工作负载是扫描密集型(而不是插入密集型),我们可能希望对该tablet进行主要的增量压缩以加快扫描速度。对于使用频繁的表进行压缩可能比在不怎么使用的表上进行压缩更聪明。
这可能是最难有效利用的信息来源,因为它涉及许多依赖于工作负载的假设和启发式方法
MemRowSet和DeltaMemRowSet对象在存储一定时间后必须刷新到磁盘。如果我们不这样做,预写日志(WAL)将不受限制地增长。这种增长会浪费磁盘空间并使启动速度变慢,因为在启动过程中必须遍历整个WAL。
我们应该在每个MemRowSets和DeltaMemRowSet中嵌入一个WAL操作ID。调度程序将更倾向于刷新存在时间比较久的MemRowSet。在操作id落后太多之后,调度程序无论如何都会尝试刷新这个MemRowSet。
这些操作需要花费一些I / O或CPU以释放内存。完成后,它们可能会提高性能。这些操作不能无限延迟,因为RAM是一种有限的资源。
开销:
效益:
其他:
基本上与MemStore刷新开销相同
其他好处:TODO:刷新也可以大大加快扫描速度。与扫描等效的memstore相比,扫描缓存文件要快得多,需要对此进行实验 。也许同一个数量级。
开销:如果驱逐了key列的缓存,查询和插入速度会变慢
效益:释放RAM这些操作消耗现在的某种I / O和CPU,以便在系统完成后提高系统性能。它们是必要的,因为如果我们不去执行它,系统最终会越来越慢。
开销:
效益:
成本:
效益:
成本:
效益:
判断是否需要压缩的相关指标:
每个tablet都会创建多个MaintenanceOp对象,表示可以对其执行的各种维护操作。它使用MaintenanceManager注册这些操作。
MaintenanceManager有一个主线程,它定期轮询已注册的MaintenanceOp对象并确定它是否应该执行它们中的任何一个。默认轮询间隔为250毫秒,但这是可配置的。假定访问MaintenanceOp是线程安全的。值得注意的是,调度程序可以选择任何可用的操作。它不一定以先到先得的方式执行操作。
如果MaintenanceManager决定执行其中一个操作,它将在可配置大小的线程池中运行它。我们假设维护操作是阻塞的并且需要线程上下文。如果操作失败,MaintenanceManager将记录警告消息并重新触发主线程。在可配置的宽限期到期之前,将不会重试失败的MaintenanceOp。
MaintenanceOp有各种字段,表明它可能释放多少内存,它将使用多少CPU,等等。它还有一个字段,标记它当前不可执行。例如,某些Ops可能会使用它,这些Ops不希望它们的多个实例同时运行。
我们希望保持至少一个线程可以自由运行刷新操作,这样我们就不会遇到这种情况:当我们需要释放内存时,所有维护操作线程都在进行压缩或其他操作。希望大多数压缩都会相当短,因此我们不必调度长时间的压缩。
转载地址:http://cwydn.baihongyu.com/