Я делаю довольно сложную находку с большим количеством включений, которые разделяются на последовательность дискретных запросов, а не на одно большое соединение. Запросы действительно медленные - мой набор данных не массивен, ни одна из таблиц не содержит более нескольких тысяч записей.
Я проиндексировал все поля, которые проверяются в запросах, но я беспокоюсь, что индексы по какой-то причине не помогают: я установил плагин под названием «query_reviewer», который просматривает запросы, используемые для построения страницы. и перечисляет проблемы с ними. Это говорит о том, что индексы не используются, и показывает результаты вызова объяснения в запросе, в котором перечислены различные проблемы. Вот пример вызова поиска:
Question.paginate(:all, {:page=>1, :include=>[:answers, :quizzes, :subject, {:taggings=>:tag}, {:gradings=>[:age_group, :difficulty]}], :conditions=>["((questions.subject_id = ?) or (questions.subject_id = ? and tags.name = ?))", "1", 19, "English"], :order=>"subjects.name, (gradings.difficulty_id is null), gradings.age_group_id, gradings.difficulty_id", :per_page=>30})
А вот сгенерированные sql запросы:
SELECT DISTINCT `questions`.id
FROM `questions`
LEFT OUTER JOIN `taggings` ON `taggings`.taggable_id = `questions`.id
AND `taggings`.taggable_type = 'Question'
LEFT OUTER JOIN `tags` ON `tags`.id = `taggings`.tag_id
LEFT OUTER JOIN `subjects` ON `subjects`.id = `questions`.subject_id
LEFT OUTER JOIN `gradings` ON gradings.question_id = questions.id
WHERE (((questions.subject_id = '1') or (questions.subject_id = 19 and tags.name = 'English')))
ORDER BY subjects.name, (gradings.difficulty_id is null), gradings.age_group_id, gradings.difficulty_id
LIMIT 0, 30
SELECT `questions`.`id` AS t0_r0 <..etc...>
FROM `questions`
LEFT OUTER JOIN `answers` ON answers.question_id = questions.id
LEFT OUTER JOIN `quiz_questions` ON (`questions`.`id` = `quiz_questions`.`question_id`)
LEFT OUTER JOIN `quizzes` ON (`quizzes`.`id` = `quiz_questions`.`quiz_id`)
LEFT OUTER JOIN `subjects` ON `subjects`.id = `questions`.subject_id
LEFT OUTER JOIN `taggings` ON `taggings`.taggable_id = `questions`.id
AND `taggings`.taggable_type = 'Question'
LEFT OUTER JOIN `tags` ON `tags`.id = `taggings`.tag_id
LEFT OUTER JOIN `gradings` ON gradings.question_id = questions.id
LEFT OUTER JOIN `age_groups` ON `age_groups`.id = `gradings`.age_group_id
LEFT OUTER JOIN `difficulties` ON `difficulties`.id = `gradings`.difficulty_id
WHERE (((questions.subject_id = '1') or (questions.subject_id = 19 and tags.name = 'English')))
AND `questions`.id IN (602, 634, 666, 698, 730, 762, 613, 645, 677, 709, 741, 592, 624, 656, 688, 720, 752, 603, 635, 667, 699, 731, 763, 614, 646, 678, 710, 742, 593, 625)
ORDER BY subjects.name, (gradings.difficulty_id is null), gradings.age_group_id, gradings.difficulty_id
SELECT count(DISTINCT `questions`.id) AS count_all FROM `questions`
LEFT OUTER JOIN `answers` ON answers.question_id = questions.id
LEFT OUTER JOIN `quiz_questions` ON (`questions`.`id` = `quiz_questions`.`question_id`)
LEFT OUTER JOIN `quizzes` ON (`quizzes`.`id` = `quiz_questions`.`quiz_id`)
LEFT OUTER JOIN `subjects` ON `subjects`.id = `questions`.subject_id
LEFT OUTER JOIN `taggings` ON `taggings`.taggable_id = `questions`.id
AND `taggings`.taggable_type = 'Question'
LEFT OUTER JOIN `tags` ON `tags`.id = `taggings`.tag_id
LEFT OUTER JOIN `gradings` ON gradings.question_id = questions.id
LEFT OUTER JOIN `age_groups` ON `age_groups`.id = `gradings`.age_group_id
LEFT OUTER JOIN `difficulties` ON `difficulties`.id = `gradings`.difficulty_id
WHERE (((questions.subject_id = '1') or (questions.subject_id = 19 and tags.name = 'English')))
На самом деле, если посмотреть на все это красиво отформатированное здесь, здесь происходит сумасшедшее количество присоединений. Это не может быть оптимальным, конечно. Во всяком случае, похоже, у меня есть два вопроса.
1) У меня есть индекс для каждого из идентификаторов и полей внешнего ключа, упомянутых здесь. Второй из вышеупомянутых запросов является самым медленным, и вызов функции объяснения для него (выполнение этого непосредственно в mysql) дает мне следующее:
+----+-------------+----------------+--------+---------------------------------------------------------------------------------+-------------------------------------------------+---------+------------------------------------------------+------+----------------------------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+----------------+--------+---------------------------------------------------------------------------------+-------------------------------------------------+---------+------------------------------------------------+------+----------------------------------------------+
| 1 | SIMPLE | questions | range | PRIMARY,index_questions_on_subject_id | PRIMARY | 4 | NULL | 30 | Using where; Using temporary; Using filesort |
| 1 | SIMPLE | answers | ref | index_answers_on_question_id | index_answers_on_question_id | 5 | millionaire_development.questions.id | 2 | |
| 1 | SIMPLE | quiz_questions | ref | index_quiz_questions_on_question_id | index_quiz_questions_on_question_id | 5 | millionaire_development.questions.id | 1 | |
| 1 | SIMPLE | quizzes | eq_ref | PRIMARY | PRIMARY | 4 | millionaire_development.quiz_questions.quiz_id | 1 | |
| 1 | SIMPLE | subjects | eq_ref | PRIMARY | PRIMARY | 4 | millionaire_development.questions.subject_id | 1 | |
| 1 | SIMPLE | taggings | ref | index_taggings_on_taggable_id_and_taggable_type,index_taggings_on_taggable_type | index_taggings_on_taggable_id_and_taggable_type | 263 | millionaire_development.questions.id,const | 1 | |
| 1 | SIMPLE | tags | eq_ref | PRIMARY | PRIMARY | 4 | millionaire_development.taggings.tag_id | 1 | Using where |
| 1 | SIMPLE | gradings | ref | index_gradings_on_question_id | index_gradings_on_question_id | 5 | millionaire_development.questions.id | 2 | |
| 1 | SIMPLE | age_groups | eq_ref | PRIMARY | PRIMARY | 4 | millionaire_development.gradings.age_group_id | 1 | |
| 1 | SIMPLE | difficulties | eq_ref | PRIMARY | PRIMARY | 4 | millionaire_development.gradings.difficulty_id | 1 | |
+----+-------------+----------------+--------+---------------------------------------------------------------------------------+-------------------------------------------------+---------+------------------------------------------------+------+----------------------------------------------+
Плагин query_reviewer имеет следующее: в нем перечислено несколько проблем:
Table questions: Using temporary table, Long key length (263), Using filesort
MySQL must do an extra pass to find out how to retrieve the rows in sorted order.
To resolve the query, MySQL needs to create a temporary table to hold the result.
The key used for the index was rather long, potentially affecting indices in memory
2) Похоже, что рельсы не разбивают эту находку оптимальным образом. Это как ты думаешь? Мне лучше делать несколько запросов на поиск вручную, а не на один большой?
Благодарен за любой совет, макс.