应用程序中的服务器错误_优化技巧,Java多线程,应用程序中的数据存储库

当执行大量查询时,数据存储库通常是高需求系统的瓶颈DelayedBatchExecutor是一个组件,它可以通过在Java多线程应用程序中批处理所需的查询来减少所需查询的数量

应用程序中的服务器错误

n查询1个参数和1个查询n个参数

让我们假设一个Java应用程序在一个关系数据库上执行一个查询来检索一个产品实体(行),给定它的唯一标识符(id)

查询如下:

现在,要检索n个产品,有两种方法:

对一个参数执行n个独立查询:

使用in运算符或n个参数的组合或对n个参数执行一个查询,以同时检索n个产品

,这在网络流量和数据库服务器资源(CPU和磁盘)方面更有效,因为

对数据库的往返次数是1而不是n。数据库引擎针对n个参数优化其数据遍历过程,即它可能只需要扫描每个表一次,而不是n次

这不仅适用于选择操作,也适用于其他操作,如插入、更新和删除。事实上,JDBC API包括这些操作的批处理操作

这也适用于NoSQL存储库,其中大部分都明确提供了批量操作需要从数据库中检索数据的

延迟批处理执行器

Java应用程序(如REST微服务或异步消息处理器)通常被实现为多线程应用程序(* 1),其中

每个线程在其执行的某个点执行相同的查询(每个查询具有不同的参数)并发线程的数量非常高(每秒几十或几百个)

在这种情况下,数据库可能会在很短的时间间隔内多次执行相同的查询

如前所述,如果带有1个参数的N个查询被带有N个参数的单个等价查询替换,应用程序将使用更少的数据库服务器和网络资源

的好消息是,它可以通过以下涉及时间窗口的机制来实现:

尝试执行查询的第一个线程将打开一个时间窗口,因此它的参数存储在列表中,并且该线程已被挂起在时间窗口内执行相同查询的其余线程将其参数添加到列表中,并且也被挂起此时,没有对数据库执行任何查询。当

时间窗口结束或列表已满(最大容量限制已预定义)时,将使用列表中存储的所有参数执行单个查询最后,一旦数据库提供了查询结果,每个线程都会收到相应的结果,所有线程都会自动恢复

我为自己构建了这个机制的简单而轻量级的实现(延迟批处理执行器),它可以很容易地在新的或现有的应用程序中使用。它基于反应器库,使用通量缓冲区发布器,通量作为参数表

使用延迟批处理执行器的吞吐量和延迟分析

让我们假设产品的REST微服务公开了从给定数据库检索产品数据productId的端点如果不使用延迟的批处理执行器,那么当它在端点达到每秒200次命中时,数据库每秒执行200次查询如果端点使用的时间窗口延迟批处理执行器被配置为50毫秒,并且最大容量= 10个参数,则数据库每秒仅执行20个查询,每个参数10个参数,但代价是每个线程最多在50毫秒内增加延迟(* 2)

换句话说,为了将等待时间增加50毫秒(* 2),数据库每秒接收的查询数减少了10倍,同时保持了系统的整体吞吐量。其他有趣的配置有

:

窗口时间= 100毫秒,最大容量= 20个参数→ 10个带有20个参数的查询(查询减少20次)窗口时间= 500毫秒,最大容量= 100个参数→2个查询。总共100个参数(查询次数减少100倍)

动作256中的延迟批处理执行器+

彻底研究产品微服务示例。假设对于每个传入的HTTP请求,微服务控制器要求我们检索提供其ID的产品(Java Bean),因此它将调用方法:

公共产品GetProductByid(整数产品ID) DAO组件产品DAO

让我们看看带有和不带有延迟批处理执行器的DAO的实现

无延迟批处理执行器

应用程序中的服务器错误

使用延迟批处理执行器

首先,延迟批处理执行器必须在DAO中创建的实例,在这种情况下为延迟批处理执行器或产品标识它需要以下三个参数:

时间窗口(本例中为50毫秒)、参数列表的最大容量(本例中为10个参数)、使用参数列表调用的方法(我们将在后面详细描述)在这个例子中,方法是retrieveProductsByIds

注意:我们稍后将看到为什么延迟批处理执行器(delayedbacheexecutor product ById)的标识是这个类的一个实例DelayedBatchExecutor2 & lt产品,整数>其次,对DAO方法PUBLIC ProductGetProductId(整数ProductId)进行了重构,可以简单地调用该实例的执行方法,这只是一个DelayedBatchExecutor ProductById。所有“魔法”都是由DelayedBatchExecutor执行的

是delayedbatchexecutior productbyid的实例,DelayedBatchExecutor2 & lt产品,整数>因为它的执行方法返回一个产品实例,并接收一个整数实例作为其参数因此,我们有:延迟数据库管理员& lt产品,整数>.

如果执行方法需要接收两个参数(如整数和字符串)并返回实例产品,则它被定义为DelayedBatchExecutor3 & lt产品,整数,字符串>。等等

最后,retrieveProductsByIds方法必须返回一个列表& lt产品>并接收一个列表<整数>;作为参数

,如果我们使用DelayedBatchExecutor3 & lt产品,整数,字符串>。,retrieveProductsByIds必须是列表& lt产品>检索产品标识(列表& lt整数> 0。产品列表,列表& lt字符串>。StringList)

是这样的一旦

运行,执行控制器逻辑的并发线程将在某个时间调用方法getProductById(整数Id),该方法将返回相应的产品他们不会知道他们实际上可能已经暂停并恢复了延期批处理执行器。

超过数据仓库

虽然本文与数据存储库相关,但延迟批处理执行器也可以用于其他上下文,例如,请求REST微服务同样,用一个参数启动N个GET请求比用N个参数启动1个GET要昂贵得多对

延迟批处理执行器的改进

我创建了一个延迟批处理执行器,并在一段时间内使用它来有效地处理个人项目中并发线程发起的多个查询的执行我相信它可能对其他人有用,所以我决定公开它。话虽如此,

仍有很大的改进空间,可以扩展为延迟批处理执行器提供的功能。最有趣的是,它可以根据执行的具体条件动态地改变参数,以延迟批处理执行器(窗口时间和最大容量),从而在使用n个参数的查询时使等待时间最小化。

大家都在看

相关专题