Hibernate 的批量获取算法是如何工作的?
Posted
技术标签:
【中文标题】Hibernate 的批量获取算法是如何工作的?【英文标题】:How does Hibernate's batch-fetching algorithm work? 【发布时间】:2011-03-29 00:04:18 【问题描述】:我在“Manning - Java Persistence with Hibernate”中找到了对批处理获取算法的描述:
什么是真正的批量抓取 算法? (...)想象一下批量大小 20个,总数119个 必须是未初始化的代理 分批加载。在启动时, Hibernate 读取映射元数据 并创建 11 个批处理加载器 内部。每个装载机知道有多少 它可以初始化的代理:20、10、9、 8, 7, 6, 5, 4, 3, 2, 1. 目标是 最小化内存消耗 loader 创建并创建足够的 每个可能的批次的装载机 fetch 可以产生。另一个目标是 尽量减少 SQL 的数量 选择,显然。初始化 119 代理 Hibernate 执行七 批次(您可能期望六个, 因为 6 x 20 > 119)。该批次 应用的装载机是五个 20次,10次,9次, 由 Hibernate 自动选择。
但我仍然不明白它是如何工作的。
-
为什么要使用 11 个批量加载器?
为什么批处理加载程序可以初始化:20、10、9、8、7、6、5、4、3、2、1 个代理?
如果有人可以提出一步一步的算法... :)
【问题讨论】:
【参考方案1】:这有助于避免创建大量不同的预处理语句。
每个查询(准备好的语句)都需要被数据库解析,其执行计划需要calculated and cached。这个过程可能比实际执行已经缓存了语句的查询要昂贵得多。
大量不同的语句可能会导致从缓存中清除其他缓存语句,从而降低整体应用程序性能。
此外,由于硬解析通常非常昂贵,因此执行多个缓存的预处理语句(包括多个数据库往返)通常比解析和执行一个新语句更快。因此,除了减少不同语句数量的明显好处之外,通过执行 11 个缓存语句来检索所有 119 个实体实际上可能比创建和执行包含所有 119 个 id 的单个新实体更快。
正如 cmets 中已经提到的,Hibernate 调用 ArrayHelper.getBatchSizes
方法来确定给定最大批量大小的批量大小。
【讨论】:
【参考方案2】:我在网上找不到任何关于 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 个加载器的加载器。
-
如果您有 33 个未初始化的代理,您可以使用以下加载程序:20、10、3
如果您有 119 个未初始化的代理,您可以使用以下加载程序,40(x2), 20, 10, 9
...
为什么批量加载器可以初始化:20、10、9、8、7、6、5、4、3、2、1个代理?
我认为 hibernate 团队选择这个作为加载“常见”数量 N 的未初始化代理和内存消耗所需的加载器数量之间的平衡。可以为 0 到 batch_size
之间的每个 N 创建一个加载器,但我怀疑加载器有相当大的内存占用所以这是一个折衷。算法可以是这样的(有根据的猜测):
n = batch_size; while (n > 10)
1.1。 loader(n); n = n / 2
for n = 0..10 create loader(n)
【讨论】:
要查看其背后的算法,您可以查看 Arrayhelper.GetBatchSizes 的源代码以上是关于Hibernate 的批量获取算法是如何工作的?的主要内容,如果未能解决你的问题,请参考以下文章
如何优化 Oracle 11g 和 Hibernate 的批量插入?
如何在 MySQL 上使用 Hibernate 批量插入递增的 ID?