【问题标题】:H2 Query performance when joining large table加入大表时的H2查询性能
【发布时间】:2014-12-29 21:51:42
【问题描述】:

我在嵌入式 H2 数据库中有以下表:

ACE (1,655,953) | PARENT_CHILD (4,544,788) | FILE (328,584)
-------------------------------------------------------------
ID              | ID                       | ID
MEMBER_ID       | PARENT_FILE_ID           | NAME
FILE_ID         | CHILD_FILE_ID            |
  • ACE 是 FILE 的访问控制条目,具有 FILE 表的外键和 MEMBER_ID 上的索引
  • PARENT_CHILD 保存文件与其所有父级(不仅仅是直接父级)之间的关系。它有两个指向 FILE 表的外键。
  • FILE 保存有关文件的信息

应用程序在树中为选定成员显示文件/ACE 信息。我需要 PARENT_CHILD 表来支持应用程序树中的延迟加载。我不会在内存中加载所有 ace/文件信息,但我只是查询适用的文件 ID。当找到与深层嵌套文件的匹配项时,加入 PARENT_CHILD 表会为我提供所有父项(树必须知道哪个根元素属于匹配文件)。我在内存中保存了一组文件 ID,用于构建应用程序文件夹树。当用户开始展开树时,我会加载有关文件和相关 ACE 的附加信息。我不想显示内容中没有任何匹配 ACE 的文件夹。但是,如果深层嵌套文件夹/文件匹配,我需要在树中呈现完整路径。

所以我使用这个查询:

select distinct parent_file_id from parent_child 
inner join ace on parent_child.child_file_id = ace.file_id 
where ace.member_id = 1;

这对于有大约 40,000 个匹配 ACE 的成员非常有效,但是当一个成员有很多匹配时,这个查询开始执行很糟糕(20 秒)。我使用 Squirrel(1536 MB java 内存,Xmx)和以下连接字符串(缓存大小 768MB)进行测试:

jdbc:h2://C:\H2DB;CACHE_SIZE=786432;QUERY_CACHE_SIZE=0;

执行计划如下:

SELECT DISTINCT
PARENT_CHILD.PARENT_ID
FROM ACE
/* ACE_MEMBER_ID_FK_INDEX_E: MEMBER_ID = 1 */
/* WHERE ACE.MEMBER_ID = 1
*/
/* scanCount: 456397 */
INNER JOIN PARENT_CHILD
/* PARENT_CHILD_FILE_ID_FILE_ID_FK_INDEX_E: CHILD_FILE_ID = ACE.FILE_ID */
/* scanCount: 6969581 */
WHERE (ACLITEM.MEMBER_ID = 1)
AND (PARENTFILE.FILE_ID = ACLITEM.FILE_ID)
/*
reads: 840206
*/

结果数:328,584。我假设磁盘上的一些临时文件导致性能下降,因为与 PARENT_CHILD 的连接会生成许多记录。我已经将 MAX_MEMORY_ROWS 增加到 10,000,000,这使得查询在 20 秒内而不是 40 秒内执行。

关于如何改进此查询的任何想法?

对成员 ID 的子查询需要 2.6 秒才能完成(不包括“读取结果”时间,456,396 条记录)。这对我来说听起来也很长,因为它只是对索引的查询:

select * from ACE where member_id = 1;

也许我必须重新考虑文件夹/文件的应用程序树的延迟加载机制。问题是,在渲染树的根元素之前,我需要知道是否有匹配的子文件。我不想为所选用户显示没有任何匹配内容的文件夹。

谢谢。

编辑:也许我会将所有父 ID 作为逗号分隔的字符串存储在 FILE 记录中。然后我不需要 PARENT_CHILD 表,在我的 Java 应用程序中,只需 0.8 秒即可将 500,000 个字符串拆分为 5,000,000 个 Int(每个字符串有 10 个逗号分隔的随机 Int 值)。

【问题讨论】:

    标签: performance resultset h2


    【解决方案1】:

    您在 parent_child(child_file_id) 上没有可用的索引。这意味着连接操作需要对 parent_child 进行全表扫描。 执行计划似乎是合理的,但无论如何,它需要嵌套循环加入 450000 x 700 万行。它很慢。 您应该在 parent_child(child_file_id) 上创建一个索引,或者切换到能够进行哈希连接的 DBMS。

    【讨论】:

    • 列 parent_child(child_file_id) 是 File 表的外键,因此自动成为索引,对吧?
    • 你看,member_id 是完全没有选择性的——大约是表大小的 1/4。然后,您对 parent_child 表进行了 400000 次索引扫描。鉴于 parent_child 表很大,系统根本不擅长在没有任何预测顺序的情况下为如此大量的访问兑现。
    • 一种可能的解决方案是尝试从 parent_child 和 ace 中删除 ID,并分别使主键为 ON (member_id, file_id) 和 (child_file_id, parent_file_id)。这应该为两个表(实际上是聚集索引)提供非常适合缓存上述查询的顺序。但无论如何,最好的解决方案是完全重新设计表结构——这都是关于用关系代数表达的层次结构。有几个选项可以做到这一点。阅读此内容,例如,link
    猜你喜欢
    • 2021-10-31
    • 1970-01-01
    • 2021-09-26
    • 1970-01-01
    • 2020-03-01
    • 1970-01-01
    • 2011-02-21
    • 1970-01-01
    • 2010-12-16
    相关资源
    最近更新 更多