Переписать mysql select для сокращения времени и записи tmp на диск - PullRequest
0 голосов
/ 21 августа 2010

У меня есть запрос mysql, который занимает несколько минут, что не очень хорошо, поскольку он используется для создания веб-страницы.

Используются три таблицы: poster_data содержит информацию об отдельных постерах.poster_categories перечисляет все категории (фильмы, рисунки и т. д.), а poster_prodcat перечисляет номер плаката и категории, в которых он может находиться, например, на одном плакате будет несколько строк, например, фильмы, Индиана Джонс, Харрисон Форд, приключенческие фильмы и т. д.

это медленный запрос:

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 - чуть более 17 миллионов.Другие запросы категорий с этим выбором едва заметны, в то время как poster_prodcat.apcatnum = '623' имеет около 400 000 результатов и записывает на диск

Ответы [ 4 ]

4 голосов
/ 21 августа 2010

надеюсь, вы найдете это полезным - 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 голосов
/ 21 августа 2010

У вас должен быть индекс на apnumber в POSTER_DATA.Сканирование 841 152 записей снижает производительность.

0 голосов
/ 21 августа 2010

Похоже, что запрос использует индекс apptitle для получения порядка, но выполняет полное сканирование для фильтрации результатов. Я думаю, что это может помочь, если у вас есть составной индекс по apptitle и apnumber для poster_data. MySQL может затем использовать это для выполнения порядка сортировки и фильтрации.

create index data_title_anum_idx on poster_data(aptitle,apnumber);
0 голосов
/ 21 августа 2010

Запрос, который вы перечислили, выглядит как окончательный запрос? (Таким образом, у них есть apcatnum = / ID /?)

где poster_data.apnumber = poster_prodcat.apnumber и poster_categories.apcatnum = poster_prodcat.apcatnum и poster_prodcat.apcatnum = '623'

poster_prodcat.apcatnum = '623' значительно уменьшит набор данных, с которыми должен работать mysql, поэтому это должна быть первая проанализированная часть запроса.

Затем перейдите, чтобы поменять местами сравнения, чтобы те, кто минимизирует набор данных, были проанализированы первыми.

Вы также можете попробовать подзапросы. Я не уверен, что это поможет, но MySQL, вероятно, сначала не получит все 3 таблицы, но сначала выполнит подзапрос, а затем другой. Это должно минимизировать потребление памяти при запросах. Хотя это не вариант, если вы действительно хотите выбрать все столбцы (так как вы используете * там).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...