Проблемы оптимизации больших запросов и структуры таблиц

Я не эксперт по БД, поэтому какое-то время я был рядом, читал столько, сколько мог, и благодаря ответам сообщества я смог внести несколько изменений в структуру моего запроса и таблиц. Даже прочитав много материала, я застрял, поэтому я пришел, чтобы задать свой первый вопрос.

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

У нас MySQL 5.x, таблицы InnoDB, сайт написан на PHP. Использование GROUP_CONCAT для возврата одной строки на историю в результате. Ранее пытался использовать идентификатор истории GROUP BY, но выполнение каждого запроса занимало около 16 секунд, это было очень медленно. С этим новым они берут 0,175, но, например, если жанр в ГДЕ не существует, запрос занимает 23 секунды! Для тестов каждая таблица имеет 1 миллион записей, таблица авторов - 1,5 миллиона. Я попытался разместить несколько дополнительных индексов, чтобы увидеть, какой из них будет использовать MySQL.

Я пытался нормализовать ситуацию с отношениями один ко многим. Здесь я приведу только несколько таблиц, так как решение, вероятно, покроет всю проблему. Любая помощь приветствуется, спасибо за ваше время!

Таблицы

CREATE TABLE `fanfiction_authors` (
  `uid` int(11) NOT NULL AUTO_INCREMENT,
  `penname` varchar(100) NOT NULL,
  `penname_url` varchar(100) NOT NULL,
  PRIMARY KEY (`uid`),
  KEY `penname_url` (`penname_url`)
) ENGINE=InnoDB  DEFAULT CHARSET=latin1 AUTO_INCREMENT=1000000 ;

-- --------------------------------------------------------

CREATE TABLE `fanfiction_stories` (
  `sid` int(11) NOT NULL AUTO_INCREMENT,
  `title` varchar(200) NOT NULL,
  `sinopse` text NOT NULL,
  PRIMARY KEY (`sid`)
) ENGINE=InnoDB  DEFAULT CHARSET=latin1 AUTO_INCREMENT=1000000 ;

-- --------------------------------------------------------

CREATE TABLE `fanfiction_stories_authors` (
  `sid` int(11) NOT NULL,
  `uid` int(11) NOT NULL,
  KEY `sid_uid` (`sid`,`uid`),
  KEY `sid` (`sid`),
  KEY `uid` (`uid`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;

-- --------------------------------------------------------

CREATE TABLE `fanfiction_stories_genres` (
  `key_id` int(11) NOT NULL AUTO_INCREMENT,
  `sid` int(11) NOT NULL,
  `genre_id` int(11) NOT NULL,
  PRIMARY KEY (`key_id`),
  KEY `sid` (`sid`),
  KEY `genre_id` (`genre_id`),
  KEY `sid_genreid` (`sid`,`genre_id`)
) ENGINE=InnoDB  DEFAULT CHARSET=latin1 AUTO_INCREMENT=1000000 ;

-- --------------------------------------------------------

CREATE TABLE `fanfiction_stories_stats` (
  `sid` int(11) NOT NULL,
  `reviews` int(11) NOT NULL,
  `recomendacoes` int(11) NOT NULL,
  PRIMARY KEY (`sid`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;

-- --------------------------------------------------------

CREATE TABLE `fanfiction_stories_warnings` (
  `key_id` int(11) NOT NULL AUTO_INCREMENT,
  `sid` int(11) NOT NULL,
  `warning_id` int(11) NOT NULL,
  PRIMARY KEY (`key_id`),
  KEY `sid` (`sid`),
  KEY `warning_id` (`warning_id`),
  KEY `warningid_sid` (`sid`,`warning_id`)
) ENGINE=InnoDB  DEFAULT CHARSET=latin1 AUTO_INCREMENT=1000000 ;

----

Запрос

SELECT  
    st.sid, st.title, st.sinopse,  
    (SELECT GROUP_CONCAT(CAST(genre_id AS CHAR)) FROM fanfiction_stories_genres WHERE sid = st.sid) as genres,  
    stats.reviews, stats.recomendacoes,  
    (SELECT GROUP_CONCAT(CAST(warning_id AS CHAR)) FROM fanfiction_stories_warnings WHERE sid = st.sid) as warnings_ids  
FROM  
    fanfiction_stories AS st  
    LEFT JOIN fanfiction_stories_stats AS stats ON st.sid = stats.sid  
WHERE  
    st.sid IN (SELECT sid FROM fanfiction_stories_warnings WHERE warning_id = 5) AND  
    st.sid IN (SELECT sid FROM fanfiction_stories_genres WHERE genre_id = 300)  
ORDER BY  
    st.sid ASC  
LIMIT 20  

Я не смог сделать свое объяснение здесь читабельным, поэтому я загрузил принтскрин в Dropbox. Не удалось вставить изображение, потому что я здесь нуб, извините.

Это объяснение расширено, когда у нас есть действительный жанр (Вы найдете истории с номером жанра 300).

explainhttp://dl.dropbox.com/u/14508898/Printscreen/stackoverflow_explain_print_001.PNG

Это объяснение расширено, когда у нас есть недопустимый жанр (вы НЕ найдете рассказы с номером жанра 900).

недопустимое объяснение жанраhttp://dl.dropbox.com/u/14508898/Printscreen/stackoverflow_explain_print_002.PNG

Не могли бы вы, ребята, помочь мне, пожалуйста? Моя нормализация правильная? Что я делаю не так?

Заранее спасибо!


person Community    schedule 02.03.2011    source источник


Ответы (1)


Вы можете сохранить 2 внутренних выбора, используя вместо этого JOIN, что, несомненно, ускорит работу в обоих случаях (genre_id = 300 и genre_id = 900).

SELECT  
    st.sid, st.title, st.sinopse,  
    (SELECT GROUP_CONCAT(CAST(genre_id AS CHAR)) FROM fanfiction_stories_genres WHERE sid = st.sid) as genres,  
    stats.reviews, stats.recomendacoes,  
    (SELECT GROUP_CONCAT(CAST(warning_id AS CHAR)) FROM fanfiction_stories_warnings WHERE sid = st.sid) as warnings_ids  
FROM  
    fanfiction_stories AS st  
    LEFT JOIN fanfiction_stories_stats AS stats ON st.sid = stats.sid  
    JOIN fanfiction_stories_warnings w ON st.sid = w.sid AND w.warning_id = 5
    JOIN fanfiction_stories_genres g ON st.sid = g.sid AND g.genre_id = 300
GROUP BY st.sid
ORDER BY st.sid ASC  
LIMIT 20  
person Galz    schedule 03.03.2011
comment
Спасибо за ваш вклад, Галз! Я пробовал это, и это увеличивает скорость, если результат оказывается пустым (получено 0,007), но когда у нас есть результаты, для возврата требуется 20 секунд. Объяснение говорит: использование временного, использование файловой сортировки. Я думаю, это из-за GROUP BY - person ; 03.03.2011
comment
@Michael - я думаю, вы можете отбросить GROUP BY и получить тот же результат, если в fanfiction_stories_warnings есть только одна запись с одним и тем же sid и warning_id и одна запись в fanfiction_stories_genres с тем же sid и жанром_id... - person Galz; 03.03.2011
comment
Да, я проводил бенчмаркинг без GROUP BY, все в порядке. Результаты в среднем 0,08 И, если нет результатов для отображения, в среднем 0,002. Большое спасибо, Гальц! Вы меня реально спасли :) - person ; 03.03.2011