mysql - 如何在这个多关联上正确添加索引?

标签 mysql ruby-on-rails ruby indexing has-many-through

我有以下模型方案:

酒店(250,000 条记录)

class Hotel < ActiveRecord::Base
  has_many :hotel_services, dependent: :destroy
  has_many :services, through: :hotel_services
end

服务(60条记录)

class Service < ActiveRecord::Base
  has_many :hotel_services
  has_many :hotels, through: :hotel_services
end

hotel_service(1,200,000 条记录)

class HotelService < ActiveRecord::Base
  belongs_to :hotel
  belongs_to :service
end

我正面临 n+1 问题。我正在运行这样的查询:

@hotels = Hotel.includes(:services).where(...)

这个查询执行得非常快(1-2 秒),但由于表 hotel_services 上有 1,200,000 百万条记录,这部分需要 30-45 秒(取决于在 where 部分)。

我在考虑使用索引来加速执行查询,但在这个方案中我应该使用哪个?

提前谢谢你们,伙计们。

编辑:hotel_services 表上添加索引:

| Table            | Non_unique | Key_name                                            | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment |
+------------------+------------+-----------------------------------------------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| hotel_services   |          0 | PRIMARY                                             |            1 | id          | A         |     1044995 |     NULL | NULL   |      | BTREE      |         |               |
| hotel_services   |          1 | index_hotel_services_on_hotel_id_and_service_id |            1 | hotel_id  | A         |      522497 |     NULL | NULL   | YES  | BTREE      |         |               |
| hotel_services   |          1 | index_hotel_services_on_hotel_id_and_service_id |            2 | service_id  | A         |     1044995 |     NULL | NULL   | YES  | BTREE      |         |               |

并生成EXPLAIN命令:

+----+-------------+------------------+-------+-----------------------------------------------------+-----------------------------------------------------+---------+------+-------+-----------------------+
| id | select_type | table            | type  | possible_keys                                       | key                                                 | key_len | ref  | rows  | Extra                 |
+----+-------------+------------------+-------+-----------------------------------------------------+-----------------------------------------------------+---------+------+-------+-----------------------+
|  1 | SIMPLE      | hotel_services   | range | index_hotel_services_on_hotel_id_and_service_id | index_hotel_services_on_hotel_id_and_service_id | 5       | NULL | 10254 | Using index condition |
+----+-------------+------------------+-------+-----------------------------------------------------+-----------------------------------------------------+---------+------+-------+-----------------------+
1 row in set (0.36 sec)

最佳答案

索引理论相当大,您应该在其他地方进一步阅读。

无论如何,对于您的特定问题,当您在 hotel_services

的两个字段上添加索引时,将会有很大的提升

在您的迁移文件中:

add_index :hotel_services, [:hotel_id, :service_id]

有时生成的索引名称太长,mysql 会报错(这不应该是这种情况,只是为了涵盖一些边缘情况)。在这种情况下,我通常将索引命名为:

add_index :hotel_services, [:hotel_id, :service_id], name: :on_foreign_keys

还有一个完全自以为是的评论:运行一个查询需要 1-2 秒的时间很多。

对于特定的查询问题,您可以使用explain 命令。

mysql> explain select * from users;
+----+-------------+-------+------+---------------+------+---------+------+------+-------+
| id | select_type | table | type | possible_keys | key  | key_len | ref  | rows | Extra |
+----+-------------+-------+------+---------------+------+---------+------+------+-------+
|  1 | SIMPLE      | users | ALL  | NULL          | NULL | NULL    | NULL |  129 |       |
+----+-------------+-------+------+---------------+------+---------+------+------+-------+

作为提示,rows 值越低越好。向数据库添加索引通常会减少数量。

要编入索引的候选人:

  • 连接子句涉及的字段
  • where子句涉及的字段

尽量避免在大表中使用 type=ALL(完全访问)

位于 select 子句中的索引元素没有帮助

关于mysql - 如何在这个多关联上正确添加索引?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27484400/

相关文章:

mysql - SQL中从多个查询到一个查询

MySQL全文索引

ruby - 创建 ruby gem

MYSQL:来自参数的条件 GROUP BY?

php - 选择数据并在所选记录中查找重复值的sql查询

ruby-on-rails - 是否有类似 'with_indifferent_access' 的数组可用于包含?

ruby-on-rails - 在 Rails 应用程序中实现 Koala Gem 时未定义的方法 `get_access_token'

ruby-on-rails - Rails Kaminari - 如何对数组进行排序/分页?

ruby-on-rails - Sinatra 对像 Controller 这样的 Web 服务的好处

ruby - 使用 Capybara/Selenium/RSpec 抑制奇怪的输出