2017-09-29 59 views
0

我有一個模式MySQL的選擇降低性能類似的查詢

applicants - id, max_res_id, max_visa_id 
applicant_files - id, applicatid, fileid, filetype 
files - id, name, filetype 

申請人 -

CREATE TABLE `applicants` (
    `id` char(36) NOT NULL, 
    `max_res_id` char(36) NOT NULL, 
    `max_visa_id` char(36) NOT NULL, 
    PRIMARY KEY (`id`), 
    KEY `idx_res_id` (`max_res_id`) USING BTREE, 
    KEY `idx_visa_id` (`max_visa_id`) USING BTREE 
) ENGINE=InnoDB DEFAULT CHARSET=utf8; 

applicant_files

CREATE TABLE `applicant_files` (
    `id` char(36) CHARACTER SET latin1 NOT NULL, 
    `applicantid` char(36) CHARACTER SET latin1 DEFAULT NULL, 
    `fileid` char(36) CHARACTER SET latin1 DEFAULT NULL, 
    `filetype` tinyint(1) DEFAULT NULL, 
    PRIMARY KEY (`id`), 
    KEY `q_applicantfile_fileid` (`fileid`), 
    KEY `u_applicantfile_applid` (`applicantid`), 
    KEY `idx_filetype` (`filetype`) USING BTREE, 
) ENGINE=InnoDB DEFAULT CHARSET=utf8; 

文件

CREATE TABLE `files` (
    `id` char(36) NOT NULL, 
    `filetype` tinyint(1) NOT NULL, 
    `name` text, 
    PRIMARY KEY (`id`), 
    KEY `idx_filetype` (`filetype`) USING BTREE, 
) ENGINE=InnoDB DEFAULT CHARSET=utf8; 

max_res_id, max_visa_id referes到applicant_files fileid 「ID」 是指files

現在 「身份證」 我有2個不同的查詢 -

select f.id as resumeId, f.name as resumeName, f.date_entered as resumeDate, 
     a.id as applId 
    from oepl_applicants a 
    inner join applicant_files af 
       ON (a.id in ('id1', 'id2') 
       and a.id = af.applicantid 
       and a.max_res_id = af.id 
       and af.filetype = 1 
       and a.max_res_id != '' 
       and a.max_res_id is not null) 
    inner join files f 
       ON (af.fileid = f.id 
       and f.filetype = 1) 
select f.id as visaId, f.name as visaName, f.date_entered as visaDate, 
     a.id as applId 
    from oepl_applicants a 
    inner join applicant_files af 
       ON (a.id in ('id1', 'id2') 
       and a.id = af.applicantid 
       and a.max_visa_id = af.id 
       and af.filetype = 2 
       and a.max_visa_id != '' 
       and a.max_visa_id is not null) 
    inner join files f 
       ON (af.fileid = f.id 
       and f.filetype = 2) 

200個IDS(ID1,ID2,... ID200) ,第一個查詢是在2秒內返回結果,而第二個查詢是在30秒內返回結果。

這裏可能會出現什麼問題?

這兩個查詢的唯一區別在於文件類型不同,連接在2個不同的列上。 PS - 與max_res_id中的值相比,max_visa_id中的很多值爲空(空)

+0

爲什麼主鍵字符串值? ...爲什麼字符串值使用不同的字符集? – Uueerdo

+0

你有列是yoru查詢,我沒有看到你的架構..(例如:f.deleted)是你確定這是正確的架構 – scaisEdge

+0

@scaisEdge:這裏顯示的模式是一個縮小版本。我的表格中有很多其他列。爲了在此討論,請考慮每個表中都有一個「已刪除」標誌。 –

回答

1

謝謝CREATE TABLEs

加入latin1 vs utf8無效索引的使用!

雖然在這種情況下無關緊要,但請將「過濾」子句移至WHERE子句,並且僅留下描述ON子句中表的相關關係的子句。例如,在第一個查詢中:

inner join applicant_files af 
       ON a.id = af.applicantid 
       and a.max_res_id = af.id 
    inner join files f ON af.fileid = f.id 
      WHERE a.id in ('id1', 'id2') 
       and f.filetype = 1 
       and af.filetype = 1 
       and a.max_res_id != '' 
       and a.max_res_id is not null 

優化程序將決定查看錶的順序。從「過濾」的條款,它希望看到這些:

a:  INDEX(max_res_id, id) 
af and f: INDEX(filetype) -- but see note below 

那麼優化器會看到,如果它是很容易得到的「下一個」表。這些可能是有益的。 (我注意到你已經有了(id)

af: INDEX(applicantid, filetype) -- in either order 

請運行EXPLAIN SELECT,看看什麼樣的順序優化摘下來的,它挑什麼指標去每個後續表。

char(36)氣味像一個UUID或GUID。這是很好,你讓他們CHARACTER SET latin1而不是utf8。但是由於隨機性,這樣的字段對於索引是很糟糕的。請參閱my blog。如果可能的話,切換到MEDIUMINT UNSIGNED AUTO_INCREMENT,儘管它會涉及很多代碼和模式更改。

filetype這兩個實例是多餘的嗎?也就是說,你是否需要檢查兩個表的文件類型?這是很多額外的工作。

爲了幫助初始查詢,我們需要了解filetype的值的分佈情況。 12更多(或更少)? 1(或2)的行是否聚集在表格的開始(或結束)附近?

桌子有多大? innodb_buffer_pool_size是什麼值?你有多少RAM?

部分或全部以下可能被密謀讓你表現不佳:

  • 的UUID的隨機性。
  • 大於的表將放在buffer_pool中。
  • 內存不足,導致buffer_pool變大。
  • buffer_pool非常大(相對於RAM),交換正在發生。

如果這些意見不能提供足夠的速度,我將推薦改制查詢延遲從f獲取:

SELECT f..., x... 
    FROM (
     SELECT ... FROM applicants AS a 
        JOIN applicant_files AS af ON ... 
       WHERE ... 
     ) AS x 
    JOIN files AS f ON x.fileid = f.id 
    WHERE f.filetype = 1 

買者:「這裏展示的架構是一個縮小的版本。」 - - 由於你的縮小,我推薦的可能不夠!

+0

非常感謝您的意見。請給我一些時間回到你身邊。我們今天有一個生產部署和錯誤修復,所以非常繁忙。欣賞你的投入。 –