MySQL慢查询优化

时间:2011-07-25 01:20:25

标签: mysql sql query-optimization

我有一个数据库~800k记录显示购票。所有表都是InnoDB。慢查询是:

SELECT e.id AS id, e.name AS name, e.url AS url, p.action AS action, gk.key AS `key`
FROM event AS e
    LEFT JOIN participation AS p ON p.event=e.id
    LEFT JOIN goldenkey AS gk ON gk.issuedto=p.person
WHERE p.person='139160'
    OR p.person IS NULL;

此查询来自PDO,因此引用了p.personJOINWHERE中使用的所有列都已编制索引。 p.event是受e.id限制的外键,而gk.issuedtop.person是受限于未提及的表person.id的外键。所有这些都是INT。表e很小 - 只有10行。表p约为500,000行,此时gk为空。

此查询在个人详细信息页面上运行。我们希望获得所有活动的列表,然后如果有参与行参与,如果有一个金钥匙行,那么他们的金钥匙。

慢查询日志给出:

Query_time: 12.391201  Lock_time: 0.000093 Rows_sent: 2  Rows_examined: 466104

EXPLAIN SELECT给出:

+----+-------------+-------+------+---------------+----------+---------+----------------+------+-------------+
| id | select_type | table | type | possible_keys | key      | key_len | ref            | rows | Extra       |
+----+-------------+-------+------+---------------+----------+---------+----------------+------+-------------+
|  1 | SIMPLE      | e     | ALL  | NULL          | NULL     | NULL    | NULL           |   10 |             |
|  1 | SIMPLE      | p     | ref  | event         | event    | 4       | msadb.e.id     |  727 | Using where |
|  1 | SIMPLE      | gk    | ref  | issuedto      | issuedto | 4       | msadb.p.person |    1 |             |
+----+-------------+-------+------+---------------+----------+---------+----------------+------+-------------+

对于给定的p.person,此查询在首次运行时运行7~12秒,然后将<0.05s。删除OR p.person IS NULL不会缩短查询时间。当p的大小从~20k增加到~500k(导入旧数据)时,此查询会减慢。

有没有人对如何提高性能有任何建议?记住总体目标是检索所有事件的列表,然后如果有参与行他们的参与,如果有一个金钥匙行,那么他们的金钥匙。如果多个查询效率更高,我可以做到。

5 个答案:

答案 0 :(得分:1)

如果您可以取消p.person IS NULL,请尝试以下操作,看看是否有帮助:

SELECT e.id AS id, e.name AS name, e.url AS url, p.action AS action, gk.key AS `key`
FROM event AS e
    LEFT JOIN participation AS p ON (p.event=e.id AND p.person='139160')
    LEFT JOIN goldenkey AS gk ON gk.issuedto=p.person

答案 1 :(得分:0)

对于笑脸...将关键字“STRAIGHT_JOIN”添加到您的选择...

SELECT STRAIGHT_JOIN   ... rest of query...

答案 2 :(得分:0)

我不确定您拥有多少索引和表格架构,但请尝试避免在默认情况下使用空值,这会大大减慢您的查询速度。

答案 3 :(得分:0)

我会尝试在参与时添加或修改索引,因此索引由两个列组成,即事件和人。

(编辑)

我的理由是,如果查询优化器不是真正的聪明,它会将事件索引用于参与,但由于你的事件很少,将扫描大量具有该事件的分区,寻找记录( s)具有where子句中的person值。通过将人员标记为参与事件索引,它为查询优化器提供了可以使用的功能,以避免使用该事件扫描所有这些参与。

答案 4 :(得分:0)

如果你正在为一个特定的人进行查找,我猜你是因为你有人ID过滤器。我会尝试反转查询,因此您首先搜索人员表,然后进行联合以及为您提供所有事件的附加查询。

SELECT 
     e.id AS id, e.name AS name, e.url AS url, 
     p.action AS action, gk.key AS `key`
FROM person AS p
    JOIN event AS e ON p.event=e.id
    LEFT JOIN goldenkey AS gk ON gk.issuedto=p.person

UNION

SELECT
    e.id AS id, e.name AS name, e.url AS url,
    NULL, NULL
FROM event AS e 

这显然意味着你有一个重复的事件,以防第一个查询匹配,但这很容易通过在整个事物周围包装一个选择,或者可能通过使用变量并在第一个查询中选择e.id来解决并在第二个查询中使用该变量(不确定这是否可行,但尚未测试,但不知道为什么不这样做)。