通过3个表的简单连接优化MySQL查询

时间:2011-11-28 19:28:36

标签: mysql optimization query-optimization

我有以下数据库表:

users
user_id (INT) (PRIMARY), is_cool (TINY) (1 or 0)

INDEX ON is_cool

activity 
activity_id (INT) (PRIMARY), user_id (INT), item_id (INT), created_at (DATETIME)

INDEX on item_id
INDEX on user_id
INDEX on created_at

items
item_id (INT) (PRIMARY), item_name VARCHAR(255), item_parent_id INT()

INDEX ON item_parent_id

我正在尝试在这些表格上进行简单的连接,并且我通过查询得到的查询时间长(> 2秒)如下:

SELECT i.item_name, a.activity_id, a.user_id, a.created_at
FROM activity as a
INNER JOIN item as i on a.item_id = i.item_id
INNER JOIN users as u on u.user_id = a.user_id
WHERE i.item_parent_id = 1 and u.is_cool = 1
ORDER by a.created_at DESC
LIMIT 0, 25

有关如何改善这一点的任何建议?由于数据库的大规模性质,它似乎悬而未决。对于较小的记录,它工作正常,但对于具有100k记录的记录 - 它需要很长时间。

2 个答案:

答案 0 :(得分:2)

此处的执行计划取决于数据库中的数据分布,但无论如何MySQL都无法在此处选择正确的索引。

<强>问题

您基本上想要遍历所有按created_at排序的活动,这些活动符合其他表中的条件。最有可能的是,MySQL会更有选择性地处理条件i.item_parent_id = 1,并从item表开始加入。也就是说,排序将在连接表中的某个字段上进行,如果连接从活动表中生成大部分行,则会导致filesorts和性能不佳。如果你向我们展示EXPLAIN的结果会更好。

<强>解决方案

如果activity JOIN item的结果产生的行数很少(比如小于1000),我只会创建以下索引:item_parent_id上已有的索引,{{1}上的复合索引用户可以使用activity(item_id, created_at)

如果(user_id, is_cool)的结果产生了很多行(更有可能),我会在activity JOIN item上创建(item_id, created_at)activity上创建(item_id, item_parent_id),{{1在item上,并在查询中添加STRAIGHT_JOIN选项:

(user_id, is_cool)

答案 1 :(得分:1)

首先,我无法在没有实际数据库的情况下对此进行测试,但我会尽可能多地将where子句移动到相应的join子句中。

这样的事情可能会有所帮助:

SELECT 
      i.item_name
    , a.activity_id
    , a.user_id
    , a.created_at
FROM activity as a
INNER JOIN item as i 
    ON  a.item_id = i.item_id
    AND i.item_parent_id = 1
INNER JOIN users as u 
    ON u.user_id = a.user_id 
    AND u.is_cool = 1
ORDER by a.created_at DESC
LIMIT 0, 25;