2010-08-20 97 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);