我有一个关于mysql(innodb)的大表,其中包含产品资产(13百万行)。这是我的数据库的一个小模式:
product <-many2one-- file_item --one2many--> family --many2one--> download_type
* file_item *表是包含数百万行的大表。我尝试使用以下sql查询按下载类型计算产品:
select t.name as type,
count(p.product_id) as n
from file_item p
inner join family f on f.id = p.family_id
inner join type t on f.id_type = t.id
group by t.id order by t.name;
* file_item * table上有3个索引:
+----+-------------+-------+--------+-----------------------------------+---------+---------+-------------------+----------+---------------------------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+-------+--------+-----------------------------------+---------+---------+-------------------+----------+---------------------------------+ | 1 | SIMPLE | p | ALL | FAMILY_IDX,PRODUCT_FAMILY_IDX | NULL | NULL | NULL | 13862870 | Using temporary; Using filesort | | 1 | SIMPLE | f | eq_ref | PRIMARY,TYPE_ID | PRIMARY | 4 | MEDIA.p.FAMILY_IDX| 1 | | | 1 | SIMPLE | t | eq_ref | PRIMARY | PRIMARY | 4 | MEDIA.f.TYPE_ID | 1 | | +----+-------------+-------+--------+-----------------------------------+---------+---------+-------------------+----------+---------------------------------+
查询需要1个多小时才能返回结果。 请问我如何优化查询?!
答案 0 :(得分:5)
这是您的原始查询:
select t.name as type,
count(p.product_id) as n
from file_item p
inner join family f on f.id = p.family_id
inner join type t on f.id_type = t.id
group by t.id order by t.name;
您需要进行两项重大更改:
MAJOR CHANGE#1:重构查询
SELECT A.ProductCount,B.name type
FROM
(
SELECT id_type id,COUNT(1) ProductCount
FROM
(
SELECT p.id_type
FROM (SELECT family_id,id_type FROM file_item) p
INNER JOIN (SELECT id FROM family) f on f.id = p.family_id
) AA
GROUP BY id_type
) A
INNER JOIN type B USING (id)
ORDER BY B.name;
重大变更#2:创建支持重构查询的索引
ALTER TABLE file_item ADD INDEX family_type_idx (family_id,id_type);
试一试!!!
答案 1 :(得分:1)
让我们将查询分解为部分:
如您所见,您的查询需要从family中获取13M行,从类型中获取13M行。
您应该开始减少执行查询所需的行提取次数:
假设f.id_type
是非NULL外键,您可以将inner join type t
更改为left join type t
。然后,将group by t.id
更改为group by f.id_type
。
对f
表而不是t
表进行分组并将内连接更改为左连接允许MySQL在从group by
获取行之前执行t
。
group by
大幅减少了行数,因此大大减少了t
的提取次数:
结果是查询已经少了13M的行。
你可以通过对模式进行非规范化来减少更多:
如果在file_item中添加family_type_id列,则可以像这样重写查询:
SELECT count(1)
FROM file_item p
JOIN type t ON t.id = p.family_type_id
GROUP BY p.family_type_id
ORDER BY t.name
对于file_item.family_type_id的索引,此查询应该以毫秒为单位执行。