Performance H2连接大表时的查询性能

Performance H2连接大表时的查询性能,performance,resultset,h2,Performance,Resultset,H2,我在嵌入式H2数据库中有以下表格: ACE (1,655,953) | PARENT_CHILD (4,544,788) | FILE (328,584) ------------------------------------------------------------- ID | ID | ID MEMBER_ID | PARENT_FILE_ID | NAME FILE_ID

我在嵌入式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是文件的访问控制项,具有文件表的外键和成员ID上的索引
  • PARENT_CHILD保持文件与其所有父级(而不仅仅是直接父级)之间的关系。它有两个指向文件表的外键
  • 文件保存有关文件的信息
应用程序在树中显示选定成员的文件/ace信息。我需要父\子表来支持应用程序树中的延迟加载。我没有在内存中加载所有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;
这对于有大约40000个匹配ACE的成员来说非常有效,但是当一个成员有很多匹配项时,这个查询开始执行得非常糟糕(20秒)。我使用Squirrel(1536mbjava内存,Xmx)和以下连接字符串(缓存大小768MB)进行测试:

jdbc:h2://C:\H2DB;缓存大小=786432;查询\缓存\大小=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
*/
结果数:328584。我假设磁盘上的某个临时文件导致了性能下降,因为与父/子连接会生成许多记录。我已经将MAX_MEMORY_行增加到10000000,这使得查询的执行时间从40秒缩短到了20秒

关于如何改进这个查询有什么想法吗

对成员id的子查询需要2.6秒才能完成(如果没有“读取结果”时间,则为456396条记录)。对我来说这听起来也很长,因为这只是对索引的查询:

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

谢谢


编辑:也许我只是将所有父id作为逗号分隔的字符串存储在文件记录中。然后我不需要表PARENT\u CHILD,在我的Java应用程序中,将500000个字符串拆分为5000000个Int(每个字符串有10个逗号分隔的随机Int值)只需0.8秒。

在PARENT\u CHILD(CHILD\u file\u id)上没有可用的索引。这意味着联接操作需要对父项和子项进行完整的表扫描。 执行计划似乎是合理的,但无论如何,嵌套循环需要连接450000x7mln行。它很慢。
您应该在父项子项(子项文件id)上创建一个索引,或者切换到能够进行哈希联接的DBMS。

列父项子项(子项文件id)是文件表的外键,因此自动成为索引,对吗?您可以看到,成员id是非选择性的—大约是表大小的1/4。然后,您有400000个索引扫描到父/子表中。鉴于父/子表很大,系统不善于在没有任何预测顺序的情况下兑现如此大规模的访问。一种可能的解决方案是尝试从父/子表和ace中删除id,并分别在(成员id、文件id)和(子/文件id、父/文件id)上生成主键。这应该为两个表(聚集索引)提供适合缓存上述查询的顺序。但无论如何,最好的解决方案是完全重新设计表结构——这都是关于用关系代数表示的层次结构。有几种方法可以做到这一点。比如读一下这个,