Медлительность SQL при выполнении поиска ActiveRecord в отношении HABTM

0

Я запускаю Rails 2.3.5 с базой данных MySQL. У меня есть отношение HABTM между Books и Users, и я пытаюсь получить всех пользователей, у которых есть указанный список книг (определенный массивом имен книг).

Я могу выполнить вызов find, который извлекает этот список пользователей:

User.find(
  :all,
  :joins      => :books,
  :conditions => { :books => { :name => book_names } }
)

Однако это оказывается чрезвычайно медленным. После игры в SQL я обнаружил, что следующий вызов работает намного быстрее и получает те же результаты:

User.find_by_sql([
  "SELECT users.* FROM users
   INNER JOIN books_users ON users.id = books_users.user_id
   WHERE books_users.book_id IN (SELECT id FROM books WHERE books.name IN (?))",
  book_names
])

Для этого же запроса вызов find занимает примерно 3000 мс на моем компьютере, тогда как вызов find_by_sql занимает примерно 200 мс; это полная разница в скорости. Я подозреваю, что виновник имеет какое-то отношение к тому факту, что исходный вызов find транслируется в двойной SQL-запрос INNER JOIN, эквивалентный следующему:

[
  "SELECT users.* FROM users
   INNER JOIN books_users ON users.id = books_users.user_id
   INNER JOIN books ON books_users.book_id = books.id
   WHERE books.name IN (?)",
  book_names
]

Мои вопросы:

  • Кто-нибудь знает, почему это так? Почему double INNER JOIN медленнее, чем мой единственный INNER JOIN с вложенным SELECT запросом?
  • Вызов find_by_sql не использует преимущества встроенной поддержки, которую Rails предоставляет для отношений HABTM. В частности, он всплывает в таблице соединений books_users, что поддержка Rails обычно абстрагируется от разработчика. Есть ли способ указать один и тот же запрос с помощью вызова find, который скрывает это?
  • 1
    Вы создали индексы в book_id и user_id для своей таблицы books_users ?
  • 0
    Ack ... Я знал, что что-то забыл. Это было оно! После добавления индексов запрос поиска теперь занимает 15 мс.
Показать ещё 1 комментарий
Теги:

2 ответа

1
Лучший ответ

После комментариев выше, кажется, что вам нужны индексы в полях book_id и user_id в books_users.

class AddIndices < ActiveRecord::Migration
  def self.up
    add_index :books_users, :book_id
    add_index :books_users, :user_id
  end

  def self.down
    remove_index :books_users, :book_id
    remove_index :books_users, :user_id
  end
end
0

Используется ли: include vs.: join делает соединение лучше?

User.find(
  :all,
  :include    => :books,
  :conditions => { :books => { :name => book_names } }
)
  • 0
    К сожалению нет; это кажется таким же медленным.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню