Проблемы оптимизации больших запросов и структуры таблиц - PullRequest
1 голос
/ 02 марта 2011

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

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

Мы работаем с MySQL 5.x, таблицы InnoDB, сайт написан на PHP. Использование GROUP_CONCAT для возврата одной строки на историю в результате. Пробовал ранее использовать идентификатор истории GROUP BY, но каждый запрос занимал около 16 секунд, был очень медленным. С этим новым они занимают 0,175, но, например, если жанр в WHERE не существует, запрос занимает 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).

explain http://dl.dropbox.com/u/14508898/Printscreen/stackoverflow_explain_print_001.PNG

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

invalid genre explain http://dl.dropbox.com/u/14508898/Printscreen/stackoverflow_explain_print_002.PNG

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

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

1 Ответ

1 голос
/ 03 марта 2011

Вы можете сохранить 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  
...