我在使用连接执行复杂查询时遇到了很多困难。
简而言之,我有一个表个人资料以及一个详细信息表 ProfileEntries 。配置文件条目组成路径/值对(确实更复杂,因为我可以有字符串条目和数字条目,但我认为这不是性能问题的根源)。我想找到某些条目匹配的所有配置文件。
list of list of uint
(抱歉,为了保密,我不得不对字符串值进行匿名处理)。这个查询是由Hibernate生成的(即使有一些更多的连接,但我简化了查询以便于分析)。如果我针对SQLServer或HSQL运行它,它就像闪电一样快,如果我对MySQL运行它就会很慢。 我已经在所有相关列上定义了索引,特别是在'path'列上,这是最具判别力的索引。 MySQL应该能够使用这些索引来优化访问。
select distinct profile0_.id as id1_7_
from SCONProfile profile0_
inner join SCONProfileEntry profileent1_ on profile0_.id=profileent1_.profile_id
inner join SCONProfileEntry profileent2_ on profile0_.id=profileent2_.profile_id
inner join SCONProfileEntry profileent3_ on profile0_.id=profileent3_.profile_id
inner join SCONProfileEntry profileent4_ on profile0_.id=profileent4_.profile_id
inner join SCONProfileEntry profileent5_ on profile0_.id=profileent5_.profile_id
inner join SCONProfileEntry profileent6_ on profile0_.id=profileent6_.profile_id
inner join SCONProfileEntry profileent7_ on profile0_.id=profileent7_.profile_id
inner join SCONProfileEntry profileent8_ on profile0_.id=profileent8_.profile_id
inner join SCONProfileEntry profileent9_ on profile0_.id=profileent9_.profile_id
inner join SCONProfileEntry profileent10_ on profile0_.id=profileent10_.profile_id
inner join SCONProfileEntry profileent11_ on profile0_.id=profileent11_.profile_id
where (profileent11_.path='AAAAAA/DDDD/Deutsch' and profileent11_.numericalValue=1 or profileent11_.path='AAAAAA/DDDD/Englisch' and profileent11_.numericalValue=1)
and (profileent10_.path='AAAAAA/WWWWW/EEE' and profileent10_.numericalValue=1 or profileent10_.path='AAAAAA/WWWWW/UUU' and profileent10_.numericalValue=1)
and profileent9_.path='AAAAAA/RRRR/WWWWW' and (profileent9_.value='Nicht notwendig' or profileent9_.value='SSSS vor Ort beteiligt' or profileent9_.value='zust. LLLL beteiligt')
and (profileent8_.path='AAAAAA/RRRR/DDDDRRRR' and profileent8_.numericalValue=1 or profileent8_.path='AAAAAA/RRRR/RRRR-RRRR' and profileent8_.numericalValue=1 or profileent8_.path='AAAAAA/RRRR/R2RRR-RRRR' and profileent8_.numericalValue=1 or profileent8_.path='AAAAAA/RRRR/IIIII' and profileent8_.numericalValue=1)
and (profileent7_.path='UUUUUUUU/KKKKKK/DDDDD' and profileent7_.numericalValue=1 or profileent7_.path='UUUUUUUU/KKKKKK/UUUUU' and profileent7_.numericalValue=1 or profileent7_.path='UUUUUUUU/KKKKKK/UUUUSSSS' and profileent7_.numericalValue=1)
and profileent6_.path='UUUUUUUU/VVVVV/VVVV' and profileent6_.numericalValue>=0 and profileent6_.numericalToValue<=20000000
and profileent5_.path='UUUUUUUU/IIIII/RRRRR' and profileent5_.value='abcd'
and profileent4_.path='UUUUUUUU/MMMMMM/DDDDRRRR' and profileent4_.numericalValue>=0 and profileent4_.numericalValue<=24
and profileent3_.path='UUUUUUUU/MMMMMM/RRRRRRRR' and profileent3_.numericalValue>=0 and profileent3_.numericalValue<=18
and profileent2_.path='UUUUUUUU/LLLL/MMMMMMMM' and profileent2_.numericalValue>=0 and profileent2_.numericalValue<=100
and (profileent1_.path='UUUUUUUU/BBBBB/A1' and profileent1_.numericalValue=1 or profileent1_.path='UUUUUUUU/BBBBB/A2' and profileent1_.numericalValue=1 or profileent1_.path='UUUUUUUU/BBBB/A3' and profileent1_.numericalValue=1)
返回
EXPLAIN SELECT ...
如果我理解正确,MySQL不使用profileent1_,profileent7_和profileent8_的路径索引,而是使用外键索引 FKpe40modwh6dhstsmypo9aub9i 。这些是连接,它们在其where-conditions中使用OR子句。
我有几个问题:
为了完整起见:以下是相关的DDL-statments
# id, select_type, table, type, possible_keys, key, key_len, ref, rows, Extra
'1', 'SIMPLE', 'profileent5_' , 'ref', 'path,value,FKpe40modwh6dhstsmypo9aub9i', 'value', '103', 'const', '1', 'Using where; Using temporary'
'1', 'SIMPLE', 'profileent6_' , 'range', 'path,FKpe40modwh6dhstsmypo9aub9i,numericalValue,numericalToValue', 'numericalToValue', '9', NULL, '16', 'Using where; Distinct; Using join buffer'
'1', 'SIMPLE', 'profileent10_', 'range', 'path,FKpe40modwh6dhstsmypo9aub9i,numericalValue', 'path', '103', NULL, '40', 'Using where; Distinct; Using join buffer'
'1', 'SIMPLE', 'profileent11_', 'range', 'path,FKpe40modwh6dhstsmypo9aub9i,numericalValue', 'path', '103', NULL, '40', 'Using where; Distinct; Using join buffer'
'1', 'SIMPLE', 'profileent4_' , 'ref', 'path,FKpe40modwh6dhstsmypo9aub9i,numericalValue', 'path', '103', 'const', '20', 'Using where; Distinct'
'1', 'SIMPLE', 'profileent9_' , 'ref', 'path,value,FKpe40modwh6dhstsmypo9aub9i', 'path', '103', 'const', '20', 'Using where; Distinct'
'1', 'SIMPLE', 'profileent2_' , 'ref', 'path,FKpe40modwh6dhstsmypo9aub9i,numericalValue', 'path', '103', 'const', '20', 'Using where; Distinct'
'1', 'SIMPLE', 'profileent3_' , 'ref', 'path,FKpe40modwh6dhstsmypo9aub9i,numericalValue', 'path', '103', 'const', '20', 'Using where; Distinct'
'1', 'SIMPLE', 'profileent1_' , 'ref', 'path,FKpe40modwh6dhstsmypo9aub9i,numericalValue', 'FKpe40modwh6dhstsmypo9aub9i', '9', 'SUPlattform.profileent9_.profile_id', '251', 'Using where; Distinct'
'1', 'SIMPLE', 'profileent7_' , 'ref', 'path,FKpe40modwh6dhstsmypo9aub9i,numericalValue', 'FKpe40modwh6dhstsmypo9aub9i', '9', 'SUPlattform.profileent1_.profile_id', '251', 'Using where; Distinct'
'1', 'SIMPLE', 'profileent8_' , 'ref', 'path,FKpe40modwh6dhstsmypo9aub9i,numericalValue', 'FKpe40modwh6dhstsmypo9aub9i', '9', 'SUPlattform.profile0_.id', '251', 'Using where; Distinct'
'1', 'SIMPLE', 'profile0_' , 'eq_ref', 'PRIMARY', 'PRIMARY', '8', 'SUPlattform.profileent5_.profile_id', '1', ''
感谢您的支持
瓦伦斯坦
答案 0 :(得分:2)
我的一位同事刚刚发现了这个问题:
它只与 where -clauses间接相关,而是与多个连接相关。如blog from Peter Zaitsev (a former developer of MySQL)中所述:查询优化的复杂性随着n的增长而增长!其中n是连接数。
因此,解决方案是建议查询优化器不要通过将 optimizer_search_depth 设置为某个有限的值来夸大优化。
瓦伦斯坦
答案 1 :(得分:0)
optimizer_search_depth=1
加速优化工具。
INDEX(profile_id, path, value),
INDEX(profile_id, path, numericalValue, numericalToValue),
INDEX(profile_id, path, toValue),
加快查询速度(部分)
如果您想进一步改进,请放弃EAV架构设计。
我的分析是否正确,OR-Clauses可能破坏连接性能? - 非常
是否有任何选项可以让MySQL提示使用更好的索引进行查询优化?或者:我可以重述此查询条件以获得更好的优化吗? - 没有
最难的一个:是否有一种“简单”的方式告诉Hibernate提供这些优化? - 我有第三方软件的选择词阻碍了。我会饶恕你的亵渎。
我为EAV添加了一个标签;仔细阅读该领域的其他问题。