【问题标题】:Use specific mysql index with rails使用带有 rails 的特定 mysql 索引
【发布时间】:2012-12-03 22:49:46
【问题描述】:

我有这个 ActiveRecord 查询

issue = Issue.find(id)
issue.articles.includes(:category).merge(Category.where(permalink: perma))

并翻译成mysql查询

SELECT `articles`.`id` AS t0_r0, `articles`.`title` AS t0_r1, 
       `articles`.`hypertitle` AS t0_r2, `articles`.`html` AS t0_r3,
       `articles`.`author` AS t0_r4, `articles`.`published` AS t0_r5,
       `articles`.`category_id` AS t0_r6, `articles`.`issue_id` AS t0_r7,
       `articles`.`date` AS t0_r8, `articles`.`created_at` AS t0_r9, 
       `articles`.`updated_at` AS t0_r10, `articles`.`photo_file_name` AS t0_r11,
       `articles`.`photo_content_type` AS t0_r12, `articles`.`photo_file_size` AS t0_r13,
       `articles`.`photo_updated_at` AS t0_r14, `categories`.`id` AS t1_r0,
       `categories`.`name` AS t1_r1, `categories`.`permalink` AS t1_r2,
       `categories`.`created_at` AS t1_r3, `categories`.`updated_at` AS t1_r4,
       `categories`.`issued` AS t1_r5, `categories`.`order_articles` AS t1_r6 
        FROM `articles` LEFT OUTER JOIN `categories` ON 
       `categories`.`id` = `articles`.`category_id` WHERE 
       `articles`.`issue_id` = 409 AND `categories`.`permalink` = 'Διεθνή' LIMIT 1

在这个查询的解释中,我看到使用了错误的索引

+----+-------------+------------+-------+---------------------------------------------------------------------------+-------------------------------+---------+-------+------+----------+-------------+
| id | select_type | table      | type  | possible_keys                                                             | key                           | key_len | ref   | rows | filtered | Extra       |
+----+-------------+------------+-------+---------------------------------------------------------------------------+-------------------------------+---------+-------+------+----------+-------------+
|  1 | SIMPLE      | categories | const | PRIMARY,index_categories_on_permalink                                     | index_categories_on_permalink | 768     | const |    1 |   100.00 |             |
|  1 | SIMPLE      | articles   | ref   | index_articles_on_issue_id_and_category_id, index_articles_on_category_id | index_articles_on_category_id | 2       | const |   10 |   100.05 | Using where |
+----+-------------+------------+-------+---------------------------------------------------------------------------+-------------------------------+---------+-------+------+----------+-------------+

我有两个索引,单独的category_idissue_id - category_id

在这个查询中,我使用issue_idcategory_id 进行搜索,使用index_articles_on_issue_id_and_category_id 比使用index_articles_on_category_id 快得多。

如何通过活动记录查询选择正确的索引?

【问题讨论】:

  • Mike 对于具体的例子,你可以只使用一个复合索引来达到相同的结果。如果您有仅使用 category_id 的查询和其他使用 category_id 和 issue_id(组合)的查询,则正确的索引将是 index_articles_on_category_id_and_issue_id。使用这样的复合索引,您可以利用这两种查询类型。看看这个stackoverflow.com/questions/1823685/…

标签: mysql ruby-on-rails ruby activerecord


【解决方案1】:

您可以像这样方便地使用索引:

 class Issue
   def self.use_index(index)
     # update: OP fixed my mistake
     from("#{self.table_name} USE INDEX(#{index})")
   end
 end

 # then
 Issue.use_index("bla").where(some_condition: true)

【讨论】:

  • 谢谢,它可以通过一个小修复 from("#{self.table_name} USE INDEX(#{index})")
  • 有没有办法在没有SQL注入风险的情况下达到类似的效果?我尝试了上面的确切方法实现,但我的 CI 中出现了 Brakeman 错误。
  • 同样是范围的形式:scope :use_index, ->(index) { from("#{table_name} USE INDEX(#{index})") }
【解决方案2】:

use_index 添加到ActiveRecord::Relation

some discussion 多年来一直在讨论将此添加到 Rails 核心,但是,看起来 PR 和分支已被放弃。

如果您知道您使用的是什么数据库以及它的局限性,您可以轻松地将其添加到您自己的代码库中以便使用。

与@krichard 的解决方案非常相似,只是更通用,适用于所有模型,而不仅仅是Issue

config/initializers/active_record_relation.rb

class ActiveRecord::Relation
  # Allow passing index hints to MySQL in case the query planner gets confused.
  #
  # Example:
  #   Message.first.events.use_index( :index_events_on_eventable_type_and_eventable_id )
  #   #=> Event Load (0.5ms)  SELECT `events`.* FROM `events` USE INDEX (index_events_on_eventable_type_and_eventable_id) WHERE `events`.`eventable_id` = 123 AND `events`.`eventable_type` = 'Message'
  #
  # MySQL documentation:
  #    https://dev.mysql.com/doc/refman/5.7/en/index-hints.html
  #
  # See https://github.com/rails/rails/pull/30514
  #
  def use_index( index_name )
    self.from( "#{ self.quoted_table_name } USE INDEX ( #{ index_name } )" )
  end
end

这将允许你使用类似的东西:

issue.articles.includes( :category ).use_index( :index_articles_on_issue_id_and_category_id )

生成的 SQL 将包括:

FROM articles USE INDEX( index_articles_on_issue_id_and_category_id )

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-05-11
    • 1970-01-01
    • 2022-11-02
    • 2015-01-17
    • 2015-08-29
    • 2012-08-29
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多