本篇文章为大家展示了如何为Java多线程应用程序优化数据存储库,内容简明扼要并且容易理解,绝对能使你眼前一亮,通过这篇文章的详细介绍希望你能有所收获。
数据存储库通常是超高要求系统的瓶颈。在这些系统中,正在执行的查询数量非常大.DelayedBatchExecutor是一个用于减少所需查询数量的组件,通过在Java多线程应用程序中对所需查询进行批处理。
<强> 1个参数的n个查询Vs。n个参数的1个查询
假设有一个对关系数据库执行查询的Java应用程序,以便在给定其唯一标识符(id)的情况下检索Product实体(row)。
查询如下所示:
SELECT * FROM PRODUCT WHERE ID=
现在,检索n个Products,有如下两种方法:
执行1个参数的n个独立查询:
SELECT * FROM PRODUCT WHERE ID = SELECT * FROM PRODUCT WHERE ID = ... SELECT * FROM PRODUCT WHERE ID =
使用IN运算符或ORs的串联,对n个参数执行1个查询以便同时检索n个 Products
-- Example using IN OPERATOR SELECT * FROM PRODUCT WHERE ID IN (, , ..., )
后者在网络流量和数据库服务器资源(CPU和磁盘)方面更为有效,因为:
往返数据库的次数为1,而不是n。
数据库引擎优化了n个参数的数据遍历过程,即每个表格可能只需要扫描1次,而不是n次。
这不仅适用于SELECT操作,而且适用于其他操作,例如 INSERTs,UPDATEs和DELETEs。实际上,JDBC API包括上述操作的批量处理操作。
同样的情况也适用于NoSQL存储库,其中大多都明确提供BULK操作。
DelayedBatchExecutor
需要从数据库中检索数据的Java应用程序,如REST微服务或异步消息处理器,通常以多线程应用程序(*1)实现,其中:
每个线程在其执行的某个时刻执行相同的查询(每个查询具有不同的参数)。
并发线程数很高(每秒数十或数百)。
在这种场景下,数据库很可能在较短的时间间隔内多次执行相同的查询。
如前所述,如果将1个参数的n个查询替换为具有n个参数的单个等效查询,那么则应用程序将使用较少的数据库服务器和网络资源。
好消息是它可以通过timewindows(时间窗口)的机制来实现,如下所示:
第一个尝试执行查询的线程会打开一个时间窗口,因此其参数被存储在一个列表中,同时该线程被暂停。在时间窗口内执行相同查询的其余线程会将其参数添加到列表中,并且也会被暂停。此时,数据库上未执行任何查询。
时间窗口结束或列表已满(先前已定义最大容量限制)后,将使用列表中存储的所有参数执行单个查询。最后,一旦数据库提供了该查询的结果,每个线程将接收相应的结果,同时所有线程将自动恢复。
笔者构建了一个简单而轻量级的应用机制(DelayedBatchExecutor),很容易在新的或现有的应用程序中使用。它基于Reactor库,并且为参数列表使用超时的Flux缓冲发布器。
运用DelayedBatchExecutor的吞吐量和延迟分析
假设针对Products的REST微服务公开了一个端点,用于检索数据库中给定的 productId的Product数据。在没有DelayedBatchExecutor的情况下,如果每秒对端点命中200次,则数据库每秒执行200个查询。如果端点使用的DelayedBatchExecutor 配置了50毫秒的时间窗口且最大容量=10个参数,数据库每秒钟将只执行10个参数的20个查询,代价是每执行一个线程,最多在50毫秒内增加延时(*2)。
换句话说,为了将延时增加50毫秒(* 2),数据库每秒接收的查询减少了10倍,然而保持了系统的整体吞吐量。还不错!!null