我有以下数据库表:
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记录的记录 - 它需要很长时间。
答案 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;