2010-08-20 56 views
0

我有一个需要几分钟的mysql查询,这不是很好,因为它用于创建网页。重写mysql选择减少时间并将tmp写入磁盘

使用三张表:poster_data包含个别海报的信息。 poster_categories列出了所有类别(电影,艺术等),而poster_prodcat列出了posterid号和它可以在例如一张海报会有多行的发言权,电影,印第安纳·琼斯,哈里森·福特,冒险电影等

这是慢查询:

select * 
    from poster_prodcat, 
     poster_data, 
     poster_categories 
where poster_data.apnumber = poster_prodcat.apnumber 
    and poster_categories.apcatnum = poster_prodcat.apcatnum 
    and poster_prodcat.apcatnum='623' 
ORDER BY aptitle ASC 
    LIMIT 0, 32 

按照解释:

explain

这需要几分钟的时间。 Poster_data有超过800,000行,而poster_prodcat超过1700万。使用此选择的其他类别查询几乎不引人注意,而poster_prodcat.apcatnum ='623'有大约400,000个结果并正在写入磁盘

回答

4

希望对您有所帮助 - http://pastie.org/1105206

drop table if exists poster; 
create table poster 
(
poster_id int unsigned not null auto_increment primary key, 
name varchar(255) not null unique 
) 
engine = innodb; 


drop table if exists category; 
create table category 
(
cat_id mediumint unsigned not null auto_increment primary key, 
name varchar(255) not null unique 
) 
engine = innodb; 

drop table if exists poster_category; 
create table poster_category 
(
cat_id mediumint unsigned not null, 
poster_id int unsigned not null, 
primary key (cat_id, poster_id) -- note the clustered composite index !! 
) 
engine = innodb; 

-- FYI http://dev.mysql.com/doc/refman/5.0/en/innodb-index-types.html 

select count(*) from category 
count(*) 
======== 
500,000 


select count(*) from poster 
count(*) 
======== 
1,000,000 

select count(*) from poster_category 
count(*) 
======== 
125,675,688 

select count(*) from poster_category where cat_id = 623 
count(*) 
======== 
342,820 

explain 
select 
p.*, 
c.* 
from 
poster_category pc 
inner join category c on pc.cat_id = c.cat_id 
inner join poster p on pc.poster_id = p.poster_id 
where 
pc.cat_id = 623 
order by 
p.name 
limit 32; 

id select_type table type possible_keys key  key_len ref       rows 
== =========== ===== ==== ============= ===  ======= ===       ==== 
1 SIMPLE  c  const PRIMARY   PRIMARY 3  const      1 
1 SIMPLE  p  index PRIMARY   name 257  null      32 
1 SIMPLE  pc  eq_ref PRIMARY   PRIMARY 7  const,foo_db.p.poster_id 1 

select 
p.*, 
c.* 
from 
poster_category pc 
inner join category c on pc.cat_id = c.cat_id 
inner join poster p on pc.poster_id = p.poster_id 
where 
pc.cat_id = 623 
order by 
p.name 
limit 32; 

Statement:21/08/2010 
0:00:00.021: Query OK 
+0

请问为什么选择innodb? (我不是真的了解差异。) – Ian 2010-08-21 01:03:04

+0

你检查了解释计划和结果查询速度吗? 聚集在一个字的索引。 – 2010-08-21 01:03:53

+0

您可能也想看看这个http:// tag1consulting。com/MySQL_Engines_MyISAM_vs_InnoDB – 2010-08-21 01:12:26

0

您列出的查询是最终查询的样子? (因此,它们具有apcatnum =/ID /?)

其中poster_data.apnumber = poster_prodcat.apnumber和poster_categories.apcatnum = poster_prodcat.apcatnum和poster_prodcat.apcatnum = '623'

poster_prodcat.apcatnum ='623 ' 将极大地减少mysql必须处理的数据集,因此这应该是查询的第一个分析部分。

然后继续交换where-comparisons,以便最大限度地减少数据集的数量最先被解析。

您可能还想尝试子查询。我不确定这会有所帮助,但是mysql可能不会首先获取所有3个表,但首先执行子查询,然后执行另一个。这应该在查询时最大限度地减少内存消耗。 虽然这不是一个选项,如果你真的想选择所有的列(因为你在那里使用*)。

+0

好吧,只是去尝试。奇怪的是,移动poster_prodcat.apcatnum ='623'是第一个返回0行的地方,而该类别中有422,777个海报。 – Ian 2010-08-20 20:48:45

0

您需要在POSTER_DATA的apnumber上有一个索引。扫描841,152条记录正在破坏性能。

+0

我的确有索引: 键名:posterid类型:唯一,基数:841152,字段:apnumber – Ian 2010-08-20 21:04:56

0

看起来像查询使用apptitle索引来获取排序,但它正在执行完整扫描以筛选结果。我认为这可能会有所帮助,如果你在poster_data的apptitle和apnumber上都有一个复合索引。 MySQL然后可以使用它来执行排序顺序和过滤器。

create index data_title_anum_idx on poster_data(aptitle,apnumber);