Hibernate的批量获取算法如何工作?

我在“Manning-Java Persistence with Hibernate”中找到了批量获取算法的描述:

什么是真正的批量获取算法? (…)想象一下批量大小为20,总共119个未初始化的代理必须分批加载。 在启动时,Hibernate读取映射元数据并在内部创建11个批处理加载器。 每个加载器知道它可以初始化多少个代理:20,10,9,8,7,6,5,4,3,2,1。目标是最小化加载器创建的内存消耗并创建足够的每个加载器可以生成可能的批量提取。 另一个目标是显着减少SQL SELECT的数量。 要初始化119个代理,Hibernate会执行7个批处理(你可能预计会有6个,因为6 x 20> 119)。 应用的批处理加载程序是Hibernate自动选择的五倍20倍,一倍10倍和一倍9倍。

但我仍然不明白它是如何工作的。

  1. 为什么11批装载机?
  2. 为什么批量加载器可以初始化:20,10,9,8,7,6,5,4,3,2,1个代理?

如果有人可以提出一步一步的算法…… 🙂

我在网上找不到有关hibernate如何处理批量加载的任何信息,但从您的信息判断,可以猜到以下内容:

为什么11批装载机?

批量大小为20时,如果要最小化任何代理组合所需的加载器数量,基本上有两个选项:

  • 为1,2,3,4,5,6,7,…创建一个加载器20,21,22,23,… N未初始化的代理(愚蠢!)或
  • 为1..9之间的任何N创建一个加载器,然后为batch_size/2创建更多加载器(递归)

示例:对于40号批次,您最终会得到40,20,10,9,8,7,6,5,4,3,2,1装载机的装载机。

  1. 如果您有33个未初始化的代理,则可以使用以下加载器:20,10,3
  2. 如果您有119个未初始化的代理,则可以使用以下加载器,40(x2),20,10,9

为什么批量加载器可以初始化:20,10,9,8,7,6,5,4,3,2,1个代理? 我认为hibernate团队选择这个作为加载未初始化代理的“常见”数N和内存消耗所需的加载器数量之间的平衡。 本可以为0和batch_size之间的每个N创建一个加载器,但我怀疑加载器有相当大的内存占用, 所以这是一个权衡 。 算法可以是这样的(有根据的猜测):

  1. n = batch_size; while (n > 10)

    1.1。 loader(n); n = n / 2

  2. for n = 0..10 create loader(n)

这有助于避免创建大量不同的预准备语句。

需要解析每个查询(预准备语句),并且需要由数据库计算和缓存其执行计划。 此过程可能比已经缓存语句的查询的实际执行要昂贵得多。

大量不同的语句可能导致从缓存中清除其他缓存的语句,从而降低整体应用程序性能。

此外,由于硬解析通常非常昂贵,因此执行多个缓存的预准备语句(包括多个数据库往返)通常比解析和执行新语句更快。 因此,除了减少不同语句数量的明显好处之外,通过执行11个缓存语句检索所有119个实体实际上比创建和执行包含所有119个ID的单个新实体更快。

正如评论中已经提到的,Hibernate调用ArrayHelper.getBatchSizes方法来确定给定最大批量大小的批量大小。