试图为MySQL查询创建索引

时间:2012-10-27 23:30:42

标签: mysql

我还没有很多MySQL和数据库方面的经验,不过我还是会继续开发大型网络应用程序。以下是我的应用程序的搜索查询,允许用户搜索其他用户。既然此查询dev_Profile的主表有大约14K行,则查询速度相当慢(运行返回最大可能集的查询时大约5秒)。我确信在这里可以进行许多优化调整,但创建索引是最基本的第一步吗?我第一次尝试自己学习索引,以及如何为具有多个连接的查询创建索引,但我只是不太了解它。我希望在我的实际查询中看到的东西可能更有教育意义。

以下是基本查询:

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)

此查询的EXPLAIN:

+----+-------------+-------------+--------+---------------+---------+---------+---------------------------------------------+-------+----------------------------------------------+
| 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子句的查询的EXPLAIN:

+----+-------------+-------------+--------+---------------+---------+---------+---------------------------------------------+-------+----------------------------------------------+
| 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还是不够。

索引是正确的步骤吗?如果是这样,有人会让我朝着正确的方向前进吗?

2 个答案:

答案 0 :(得分:1)

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

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

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

答案 1 :(得分:0)

应该使用与频繁查询相关的索引。索引会略微降低写入性能,同时极大地提高了搜索速度。根据经验,对象自己的ID应该被索引为PRIMARY键,并且最好在列组中创建一个始终在查询中出现的索引。我想你应该索引GenderID,CountryID,RegionID,CityID,age,online和hasPhoto。如果您认为未使用正确的索引,则应至少提供dev_Profile的模式。

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

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