2016-01-12 16 views
1

對於具有連接的複雜查詢的性能,我有相當的難度。性能: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的第二部分和第三部分(並且 獲得的結果較少),則查詢會再次快速。

我有幾個問題:

  1. 是我的分析是正確的,即OR-條款可能會破壞連接性能?
  2. 是否有任何選擇讓MySQL提示使用更好的索引進行查詢優化?或者:我可以重新聲明這個查詢條件以獲得更好的優化嗎?
  3. 最難的一個:是否有一種「簡單」的方式來告訴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; 

感謝您對您的支持

華倫斯坦

+1

MySQL只在查詢中的表中使用一個索引,因此它不得不選擇是使用profile_id上​​的索引來幫助連接,還是使用路徑上的索引來縮小連接後的結果。可以強制索引(http://dev.mysql.com/doc/refman/5.7/en/index-hints.html),但最好在profile_id,path和numericValue上都有覆蓋索引。 – Kickstart

回答

2

我的一個同事剛剛發現的問題:

它只是間接關係到其中條款,而是涉及多個連接。正如blog from Peter Zaitsev (a former developer of MySQL)中提到的那樣:查詢優化的複雜性隨n增長而增加!其中n是連接的數量。

因此,解決方案是,建議查詢優化器不要誇大優化,通過將optimizer_search_depth設置爲一些有限的值。

瓦倫斯坦

+0

要證明Zaitsev在說什麼......當所有的表都有相似的統計數據時,複雜性會增長。就你而言,它們都具有完全相同的統計數據,因爲它們都是相同的。其中存在鍵值(EAV)架構模型的一個難題。 –

+0

是的設置'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-子句可能會破壞連接性能? - 非常多

有什麼選擇讓MySQL提示使用更好的索引進行查詢優化嗎?或者:我可以重新聲明這個查詢條件以獲得更好的優化嗎? - 否

最難的是:是否有一種「簡單」的方法來告訴Hibernate提供這些優化? - 對於阻礙第三方軟件的選擇詞彙。我會饒恕你的褻瀆。

我已經爲EAV添加了一個標籤;通讀該領域的其他問題。