對於具有連接的複雜查詢的性能,我有相當的難度。性能:MySQL中的OR條件顯着減慢內部聯接?
概括地說,我有一個表檔案連同詳細信息表ProfileEntries。 Profileentries由路徑/值對組成(確實比slighlty更復雜,因爲我可以有字符串條目和數字條目,但我認爲這不是性能問題的來源)。我想查找某些條目匹配的所有配置文件。
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)
(對不起,出於保密的原因,我不得不匿名字符串值)。這個查詢是由Hibernate生成的(即使有更多的連接,但我簡化了查詢以便於分析)。如果我使用SQLServer或HSQL運行它,它就像雷電一樣快,如果我對它運行它,它會變得很慢。 我已經定義了所有相關列上的索引,尤其是在「路徑」列上,這是最具歧視性的索引。 MySQL應該能夠使用這些索引來優化訪問。
EXPLAIN SELECT ...
回報
# 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', ''
如果我理解正確的話,MySQL是不使用路徑指數profileent1_,profileent7_和profileent8_而是外鍵索引FKpe40modwh6dhstsmypo9aub9i。那些是在它們的條件下使用OR-子句的連接。
- 如果我用等價的IN-clause替換OR-Clause並沒有幫助,結果相同。
- 如果我只是剔除OR-Clause的第二部分和第三部分(並且 獲得的結果較少),則查詢會再次快速。
我有幾個問題:
- 是我的分析是正確的,即OR-條款可能會破壞連接性能?
- 是否有任何選擇讓MySQL提示使用更好的索引進行查詢優化?或者:我可以重新聲明這個查詢條件以獲得更好的優化嗎?
- 最難的一個:是否有一種「簡單」的方式來告訴Hibernate提供這些優化?
爲了完整起見:這裏是相關DDL-statments
CREATE TABLE `SCONProfileEntry` (
`EntryType` varchar(1) NOT NULL,
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`path` varchar(100) DEFAULT NULL,
`toValue` varchar(16) DEFAULT NULL,
`value` varchar(100) DEFAULT NULL,
`deal_id` int(11) DEFAULT NULL,
`profile_id` bigint(20) DEFAULT NULL,
`numericalToValue` bigint(20) DEFAULT NULL,
`numericalValue` bigint(20) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `path` (`path`),
KEY `value` (`value`),
KEY `toValue` (`toValue`),
KEY `FKeqdw6j0h25txycjtdhotbv6yl` (`deal_id`),
KEY `FKpe40modwh6dhstsmypo9aub9i` (`profile_id`),
KEY `numericalValue` (`numericalValue`),
KEY `numericalToValue` (`numericalToValue`),
CONSTRAINT `FKeqdw6j0h25txycjtdhotbv6yl` FOREIGN KEY (`deal_id`) REFERENCES `SCONDeal` (`id`),
CONSTRAINT `FKpe40modwh6dhstsmypo9aub9i` FOREIGN KEY (`profile_id`) REFERENCES `SCONProfile` (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=49963 DEFAULT CHARSET=latin1;
CREATE TABLE `SCONProfile` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`institut_BLZ` varchar(8) DEFAULT NULL,
`user_id` int(11) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `FKgjcl2p9ws1h72mibx813g5o67` (`institut_BLZ`),
KEY `FKrlnujjqbbivdouaa1unp1yvxj` (`user_id`),
CONSTRAINT `FKgjcl2p9ws1h72mibx813g5o67` FOREIGN KEY (`institut_BLZ`) REFERENCES `InstitutsData` (`BLZ`),
CONSTRAINT `FKrlnujjqbbivdouaa1unp1yvxj` FOREIGN KEY (`user_id`) REFERENCES `UserData` (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2299 DEFAULT CHARSET=latin1;
感謝您對您的支持
華倫斯坦
MySQL只在查詢中的表中使用一個索引,因此它不得不選擇是使用profile_id上的索引來幫助連接,還是使用路徑上的索引來縮小連接後的結果。可以強制索引(http://dev.mysql.com/doc/refman/5.7/en/index-hints.html),但最好在profile_id,path和numericValue上都有覆蓋索引。 – Kickstart