微批量

时间:2020-01-09 10:36:18  来源:igfitidea点击:

微批处理是一种将要执行的传入任务分组为小批处理的技术,以实现批处理的某些性能优势,而又不会增加每个任务完成的延迟。微批处理通常应用于传入任务数量可变的系统。系统将抓取已收到的所有传入任务并批量执行它们。重复执行该过程。

批量大小因此可以从1到系统设置的最大上限之间变化,例如64、128、1024或者适用于系统的任何最大批处理大小。通常,由于我将在下面解释的原因(因此称为术语微分批处理),最大批处理大小很小。

延迟与吞吐量的权衡

面向服务的系统通常需要低延迟和高吞吐量。但是,这并不总是可能的。一些减少延迟的技术也会降低吞吐量,而一些增加吞吐量的技术也会增加延迟。在以下各节中,我将更详细地说明这一点。

延迟

延迟是系统中时间延迟的度量。在客户端服务器系统中,延迟可能意味着几件事。网络等待时间是客户端发送一条消息直到到达服务器所花费的时间。服务器等待时间是服务器处理请求并生成响应所花费的时间。两种类型的延迟如下所示:

直到客户端收到响应为止,单个请求的完整往返时间为

network latency + server latency + network latency  =

2 * network latency + server latency

首先必须将请求发送到服务器,然后服务器必须处理该请求并生成响应,然后必须通过网络将响应发送回客户端。

为了使系统具有快速的响应时间,网络延迟和服务器延迟都必须较低。究竟"快速"和"缓慢"的响应时间,还是"高"和"低"的延迟时间取决于具体的系统。对于某些系统,响应时间少于1秒是好的。对于某些系统来说,它必须小于10毫秒才能达到最佳状态。

吞吐量

吞吐量是衡量系统在给定时间间隔内可以执行多少工作的量度。在客户端服务器系统的情况下,服务器吞吐量衡量服务器每个时间间隔(通常为每秒)可以处理多少个请求。此数字表示服务器每秒可以从所有连接的客户端(而不仅仅是单个客户端)处理的请求总数。

客户端看到的吞吐量表示特定时间段内每个客户端可以发送和接收响应的请求数量。两种类型的吞吐量如下图所示:

批处理

批处理是一种增加系统吞吐量的技术。不必将每个任务分别执行,而是将这些任务分组为大批并一起执行。

如果可以通过分批执行来减少与执行每个任务相关的开销,则在这种情况下分批处理是有意义的。要查看如何做,让我们看一个例子:

假设一个客户端有10个请求需要发送到服务器。客户端可以一次发送1个请求,接收响应,然后发送下一个请求。处理这些消息所需的总时间为:

10 * (network latency + server latency + network latency) =

20 * network latency + 10 * server latency

相反,如果客户端在一条消息中将所有10个请求发送到服务器,并且服务器按顺序处理所有请求,并发送回10个响应,则处理这些请求所需的总时间为:

network latency + 10 * server latency + network latency =

2 * network latency + 10 * server latency

如我们所见,批处理将处理10个请求所涉及的网络延迟从20 *网络延迟减少到仅2 *网络延迟。这意味着客户端所看到的客户端-服务器系统的总吞吐量已增加。

批处理的缺点是收集要分批处理的任务所花费的时间。如果客户端花了2个小时来收集这10个任务,那么整个系统的等待时间就变得很高。第一个任务需要等待2个小时,然后才能有足够的任务发送批处理,这意味着从收集第一个任务开始,直到客户端收到响应为止需要2个小时。

类似地,一旦发送了批处理,服务器将花费10 *服务器等待时间来处理该批处理。这进一步增加了第一个请求的等待时间,因为在接收到第一个请求的响应之前,它必须等待所有10个请求都得到处理。

如我们所见,批处理是一种提高吞吐量但也增加延迟的技术。

微型批次救援

微批处理是批处理的一种变体,与批处理相比,微批处理试图在延迟和吞吐量之间取得更好的折衷。微批处理的方法是等待较短的时间间隔以在处理任务之前对任务进行批处理。我将此间隔称为批处理周期。此处说明了这种短批处理周期的原理:

批处理周期的持续时间应取决于系统。对于某些系统,一秒钟可能就足够了。对于其他系统,50 100毫秒可能很好。而对于其他系统则更少。

如果系统上的负载很高,它将在每个批处理周期内收到更多准备处理的任务。因此,随着系统上的负载增加,批次大小增加,并且吞吐量增加。当批量增加时,以更高的延迟为代价的价格是最小的。

可变持续时间批处理周期

对于要求低响应时间的系统,即使50毫秒的批处理周期持续时间也可能太长。这样的系统可能需要使用可变的批处理周期长度。

为了实现较低的延迟,同时又允许进行微批处理,我们可以循环输入通道(入站网络连接,目录等),并检查所有输入通道是否有传入任务(请求,消息等)。无论我们发现什么任务,我们都将在微型批次中执行。通过此循环的每次迭代都将成为单个批处理周期。

一旦执行了微型批处理,就可以立即重复循环。这意味着每个批处理周期之间的时间完全取决于传入任务的数量。对于低负载,批次大小会很小,因此批次周期会更短。对于高负载,批次大小将增加,因此批次周期持续时间将增加。

此处显示了可变持续时间的批处理周期:

微批量用例

微型批处理可用于许多可以使用批处理但需要较短响应时间的情况。在以下各节中,我将介绍其中一些用例,但是这些用例不是唯一的。从这些用例中,我们应该能够大致了解情况,并能够确定何时在我们自己的系统中微批处理可能有用。

文件持久性

将数据写入磁盘通常会产生大量开销。如果系统需要为每个执行的任务将数据块写入磁盘,则总开销可能会很大。

如果分批写入,因此仅将合并的数据块写入磁盘,则与写入较大的数据块相关的开销通常小于单独进行写入的合并开销。结果是系统可以处理更大的吞吐量(每个时间单位写入更多数据)。

使用微批处理实现文件持久性通常需要将系统的其余部分设计为也使用微批处理。如果数据块一次到达一个文件持久性组件,则将它们分组的唯一方法是等待较短的时间间隔,然后再将它们写入磁盘。如果数据块是微批次到达的,因为它们是在其他地方进行微批次处理的结果,则将它们分组为微批次以将它们写入磁盘要容易得多。

线程间通讯

当线程进行通信时,它们通常通过并发数据结构进行通信。为此目的,经常使用的结构是并发队列。这在这里说明:

与在队列中读取和写入元素相比,一次读取一个元素通常与每个元素的开销更高。我们可以在我的有关Java Ring Buffer的教程中阅读有关此内容的更多信息。环形缓冲区也可以用作队列。通过队列发送成批的消息如下所示:

进程间通讯

进程间通信在许多方面类似于线程间通信。当进程进行通信时,通常会产生与在进程外将数据发送到磁盘,Unix套接字或者网络套接字相关的开销。因此,有益的是,对在进程外发送的数据进行批处理以最小化每个数据块(例如,每个请求,消息,任务等)的开销。

在本教程的前面,我已经解释了批处理如何在客户端-服务器场景中提供帮助,这是进程间通信的一种常见情况。

作为对该事实的进一步证明,数据库批处理更新通常比分别将每个更新发送到数据库要快得多。通过批量更新,可以通过网络批量发送更多更新,并且数据库也可能有机会将更新作为批量操作写入磁盘。

单线程服务器

单线程服务器体系结构由于其非常简单的并发模型(一切都在同一线程中运行),如今正重新流行,因此与多线程并发模型相比,它在许多情况下可以更好地利用CPU缓存。我的有关并发模型的教程中也提到了这一点。

单线程服务器通常会轮询所有打开的入站连接以读取数据。如果入站连接具有入站数据,则将对其进行读取和处理。

单线程服务器可以从微批处理中受益。单线程服务器无需一次从一个连接中读取和处理一条消息,而是从所有入站连接中读取所有完整消息并进行批处理。

在单线程服务器中使用微批处理设计使该系统的其他部分(在同一服务器内运行)也更容易使用微批处理。

遍历大数据结构

某些应用程序可能需要遍历存储在内存或者磁盘上的大型数据结构。例如,数据库表或者树结构。遍历大型数据结构存在一定的开销。对于基于内存的数据结构,必须将数据从主存储器带入CPU L1高速缓存,对于基于磁盘的数据结构,必须将数据从磁盘带入主存储器,然后从主存储器带入L1高速缓存。

我们可以遍历几个请求或者任务,而不必遍历整个数据结构来仅满足一个"请求"(任务,消息等)。在遍历数据结构时,可以根据一批请求,而不仅仅是单个请求,来检查数据结构中的每个"记录"或者"节点"。

如果这些请求中的任何一个对数据结构进行了更改,则这些请求应以从客户端接收到的相同顺序访问该数据结构。这样,即使请求1尚未完全完成对整个数据结构的更新,请求2也会看到数据结构的每个记录/节点,就像请求1会离开它一样。