2012-10-27 51 views
1

尽管我正在开发一个大型Web应用程序,但我还没有很多MySQL和数据库方面的经验。以下是我的应用的搜索查询,它允许用户搜索其他用户。现在,此查询dev_Profile的主表具有大约14K行,查询速度相当慢(运行查询时会返回最大集合大约5秒)。我确定这里可以做许多优化调整,但是创建索引是这里要做的最基本的第一步?我一直试图自己学习索引,以及如何为具有多个连接的查询创建索引,但我只是不太明白。我希望在我的实际查询中看到事物可能更具教育意义。试图为MySQL查询创建索引

这里的基本查询:

SELECT 
    dev_Profile.ID AS pid, 
    dev_Profile.Name AS username, 
    IF(TIMESTAMPDIFF(SECOND, st1.lastActivityTime, UTC_TIMESTAMP()) > 300 OR ISNULL(TIMESTAMPDIFF(SECOND, st1.lastActivityTime, UTC_TIMESTAMP())), 0, 1) AS online, 
    FLOOR(DATEDIFF(CURRENT_DATE, dev_Profile.DOB)/365) AS age, 
    IF(dev_Profile.GenderID=1, 'M', 'F') AS sex, 
    IF(ISNULL(st2.Description), 0, st2.Description) AS relStatus, 
    st3.Name AS country, 
    IF(dev_Profile.RegionID > 0, st4.Name, 0) AS region, 
    IF(dev_Profile.CityID > 0, st5.Name, 0) AS city, 
    IF(ISNULL(st6.filename), 0, IF(st6.isApproved=1 AND st6.isDiscarded=0 AND st6.isModerated=1 AND st6.isRejected=0 AND isSizeAvatar=1, 1, 0)) AS hasPhoto, 
    IF(ISNULL(st6.filename), IF(dev_Profile.GenderID=1, 'http://www.mysite.com/lib/images/avatar-male-small.png', 'http://www.mysite.com/lib/images/avatar-female-small.png'), IF(st6.isApproved=1 AND st6.isDiscarded=0 AND st6.isModerated=1 AND st6.isRejected=0 AND isSizeAvatar=1, CONCAT('http://www.mysite.com/uploads/', st6.filename), IF(dev_Profile.GenderID=1, 'http://www.mysite.com/lib/images/avatar-male-small.png', 'http://www.mysite.com/lib/images/avatar-female-small.png'))) AS photo, 
    IF(ISNULL(dev_Profile.StatusMessage), IF(ISNULL(dev_Profile.AboutMe), IF(ISNULL(st7.AboutMyMatch), 0, st7.AboutMyMatch), dev_Profile.AboutMe), dev_Profile.StatusMessage) AS text 
FROM 
    dev_Profile 
    LEFT JOIN dev_User AS st1 ON st1.ID = dev_Profile.UserID 
    LEFT JOIN dev_ProfileRelationshipStatus AS st2 ON st2.ID = dev_Profile.ProfileRelationshipStatusID 
    LEFT JOIN Country AS st3 ON st3.ID = dev_Profile.CountryID 
    LEFT JOIN Region AS st4 ON st4.ID = dev_Profile.RegionID 
    LEFT JOIN City AS st5 ON st5.ID = dev_Profile.CityID 
    LEFT JOIN dev_Photos AS st6 ON st6.ID = dev_Profile.PhotoAvatarID 
    LEFT JOIN dev_DesiredMatch AS st7 ON st7.ProfileID = dev_Profile.ID 
WHERE 
    dev_Profile.ID != 11222 /* $_SESSION['ProfileID'] */ 
    AND st1.EmailVerified = 'true' 
    AND st1.accountIsActive=1 
ORDER BY st1.lastActivityTime DESC LIMIT 900; 

此查询的速度(太慢了,你可以看到):

900 rows in set (5.20 sec) 

的解释此查询:

+----+-------------+-------------+--------+---------------+---------+---------+---------------------------------------------+-------+----------------------------------------------+ 
| id | select_type | table  | type | possible_keys | key  | key_len | ref           | rows | Extra          | 
+----+-------------+-------------+--------+---------------+---------+---------+---------------------------------------------+-------+----------------------------------------------+ 
| 1 | SIMPLE  | dev_Profile | range | PRIMARY  | PRIMARY | 4  | NULL          | 13503 | Using where; Using temporary; Using filesort | 
| 1 | SIMPLE  | st2   | eq_ref | PRIMARY  | PRIMARY | 1  | syk.dev_Profile.ProfileRelationshipStatusID |  1 |            | 
| 1 | SIMPLE  | st3   | eq_ref | PRIMARY  | PRIMARY | 4  | syk.dev_Profile.CountryID     |  1 |            | 
| 1 | SIMPLE  | st4   | eq_ref | PRIMARY  | PRIMARY | 4  | syk.dev_Profile.RegionID     |  1 |            | 
| 1 | SIMPLE  | st5   | eq_ref | PRIMARY  | PRIMARY | 4  | syk.dev_Profile.CityID      |  1 |            | 
| 1 | SIMPLE  | st1   | eq_ref | PRIMARY  | PRIMARY | 4  | syk.dev_Profile.UserID      |  1 | Using where         | 
| 1 | SIMPLE  | st6   | eq_ref | PRIMARY  | PRIMARY | 4  | syk.dev_Profile.PhotoAvatarID    |  1 |            | 
| 1 | SIMPLE  | st7   | ALL | NULL   | NULL | NULL | NULL          | 442 |            | 
+----+-------------+-------------+--------+---------------+---------+---------+---------------------------------------------+-------+----------------------------------------------+ 

查询可能还有更多WHEREHAVING子句,如果用户搜索包含其他条件。附加子句(设定示例值):

AND dev_Profile.GenderID = 1 
AND dev_Profile.CountryID=127 
AND dev_Profile.RegionID=36 
AND dev_Profile.CityID=601 
HAVING (age >= 18 AND age <= 50) 
AND online=1 
AND hasPhoto=1 

这是利用一切可能WHEREHAVING条款的解释查询:

+----+-------------+-------------+--------+---------------+---------+---------+---------------------------------------------+-------+----------------------------------------------+ 
| id | select_type | table  | type | possible_keys | key  | key_len | ref           | rows | Extra          | 
+----+-------------+-------------+--------+---------------+---------+---------+---------------------------------------------+-------+----------------------------------------------+ 
| 1 | SIMPLE  | dev_Profile | range | PRIMARY  | PRIMARY | 4  | NULL          | 13503 | Using where; Using temporary; Using filesort | 
| 1 | SIMPLE  | st2   | eq_ref | PRIMARY  | PRIMARY | 1  | syk.dev_Profile.ProfileRelationshipStatusID |  1 |            | 
| 1 | SIMPLE  | st3   | const | PRIMARY  | PRIMARY | 4  | const          |  1 |            | 
| 1 | SIMPLE  | st4   | const | PRIMARY  | PRIMARY | 4  | const          |  1 |            | 
| 1 | SIMPLE  | st5   | const | PRIMARY  | PRIMARY | 4  | const          |  1 |            | 
| 1 | SIMPLE  | st1   | eq_ref | PRIMARY  | PRIMARY | 4  | syk.dev_Profile.UserID      |  1 | Using where         | 
| 1 | SIMPLE  | st6   | eq_ref | PRIMARY  | PRIMARY | 4  | syk.dev_Profile.PhotoAvatarID    |  1 |            | 
| 1 | SIMPLE  | st7   | ALL | NULL   | NULL | NULL | NULL          | 442 |            | 
+----+-------------+-------------+--------+---------------+---------+---------+---------------------------------------------+-------+----------------------------------------------+ 

我甚至不知道这是TMI还是不够。

索引是采取正确的措施吗?如果是这样,有人能让我朝着正确的方向前进吗?

+0

它看起来好像你有外键设置,这是很好的:)另一个索引表将'st1'我相信'(lastActivityTime,emailVerified,accountIsActive)'。 –

回答

1

正确的一步是加快您的查询速度!

用你原来的查询,我会说你最终在dev_Profile表上做表扫描,因为它没有可索引条件。使用修改后的查询,它取决于列中允许的不同值的数量 - 如果有可能重复,则索引可能无法使用,因为无论如何它必须获取表以完成查询的其余部分。

我已经正确地阅读了您的计划,然后您已经将所有其他表加入已索引的不可为空的列(除了st7,由于某种原因似乎没有使用索引)。因此它看起来好像你不应该使用左连接。这将允许在表st1上使用(EmailVerified, accountIsActive, lastActivityTime)上的索引。

0

应该使用与频繁查询相关的索引。一个索引稍微降低了写入性能,同时极大地提高了搜索速度。作为一个经验法则,对象自己的ID应该作为PRIMARY键索引,并且在查询中总是出现一个列组的索引是一个好主意。我想你应该索引GenderID,CountryID,RegionID,CityID,年龄,在线和hasPhoto。如果您认为没有使用正确的索引,则应该至少提供dev_Profile的模式。

请注意,国家/地区/城市ID可能代表冗余信息。您的设计可能不够理想。

注意2:你在SELECT中做了很多应用程序逻辑。 SQL不是为这些大量的IF-in-IF-in-IF子句设计的,而且由于URL的查询返回的表格比如果你刚刚请求相关字段(例如文件名,genderID等等上)。可能有时候查询必须返回这些精确的解释值,一般而言,您最好(在速度和可读性方面)将这些处理步骤编码到您的应用程序代码中。