ClassTypeInformation.from(..) 在 MappingMongoConverter 中的使用引入了很多线程争用

Usage of ClassTypeInformation.from(..) inside MappingMongoConverter introduces much thread contention

我正在编写一个应用程序,它通过 Spring 数据的 MongoTemplate 将大量对象存储到 MongoDB 中。为了避免同步,我为每个线程创建了一个单独的 MongoTemplate。事实上,每个线程都有自己的一切以避免同步。

应用程序处理用户与网页交互时生成的事件。因此,来自特定用户的事件需要按顺序处理,而跨多个用户的事件可以并行处理。该应用程序当前由 N 个管道和一个负载均衡器组成,该负载均衡器根据 hash/mod 的 userId 将事件分发到管道。在管道的末端,使用 Spring 数据 MongoDB 模板将数据写入 MongoDB。

这个单个进程能够处理 2500 个 events/second。但是,我观察到线程之间存在显着的争用(以 2500 events/second 的速度,任何阻塞都变得相当重要)。全部在访问同步缓存的 ClassTypeInformation 区域。

不幸的是,MongoTemplate 使用 ClassTypeInformation 将缓存存储在同步地图中。因此,无论我如何尝试,将数据写入 MongoDB 总是会引起我的工作线程之间的争用。

我认为 ClassTypeInformation 应该转换成一个 bean,以便在用户需要时提供一个。考虑到这一点,将消除多个线程之间的争用。

有谁知道为什么将其实现为静态 bean 而不是普通的 Spring bean?是否有任何计划进行此更改?

没有,没有计划。出于多种原因:ClassTypeInformation 是一个值对象,而不是可注入对象。必须非常频繁地即时创建实例。用 Spring 配置它的实例根本没有意义,因为通常当我们遇到某种 Class 时必须创建实例,需要检查它并维护和解析泛型信息.使用静态工厂确保我们可以对对象创建应用改进,而无需修改所有客户端。

我们应用了大量基准测试和调整来消除最新版本中的一些性能瓶颈,ClassTypeInformation 甚至从未接近于在分析器分析中出现。

一般来说,我会在深入研究底层内部结构之前开始:

  • 您认为首先需要开始并行化插入的原因是什么。如果您想利用我们的对象到文档映射工具,MongoTemplate.insertAll(…) 基本上是将对象插入 MongoDB 的最快方式。引入并行性实际上可以减慢速度,因为首先需要同步。
  • 为什么您认为创建单独的 MongoTemplate 实例会有所改善? MongoTemplate 是线程安全的,因此可以在线程之间共享。
  • 您是否考虑过将手动将对象转换为 DBObject 的自定义转换器?默认情况下,我们必须使用反射将前者转换为后者,这当然是有代价的。
  • 您真的需要首先从对象开始吗?有时从输入源读取的数据可以直接映射到 DBObjects,这允许您完全绕过对象到文档的映射。

一般来说,使用数据访问的所有便利性 API 通常与获得大部分性能的目标背道而驰。与其拘泥于便利并尝试并行化所有内容,通常更容易摆脱一些便利以避免代价高昂的代码路径。

如果您真的发现默认设置有任何问题,我们很乐意提交一份错误报告,并附上您发现的一些证据(可执行测试用例等),看看我们可以改进什么。但我认为从简单开始而不是一开始就尝试太聪明是有帮助的。