我有以下模型方案:
酒店(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秒),但由于表中的有很多关系和1,200,000,000条记录 hotel_services ,此部分需要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)
答案 0 :(得分:1)
指数理论非常大,你应该在其他地方进一步阅读。
无论如何,对于您的特定问题,当您在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
值越低越好。向DB添加索引通常会减少数量。
被编入索引的候选人:
尽量避免在大表中使用type = ALL(完全访问权限)
位于select
子句中的索引元素无用
答案 1 :(得分:0)
两个主表的ID上的常用键对于这些表就足够了。我倾向于在链接表上添加包含hotel_id和service_id的双字段索引,而不是在每个字段上添加一个。
它很容易测试,所以玩一下。在进行最终迁移之前,在mysql(或特定数据库品牌的任何工具)中手动添加它们
答案 2 :(得分:0)
sql
查询,则Rails应用程序将获得显着的性能提升。除非绝对必要,否则不得使用请求来访问数据库。
<强> 1。在所有外键上添加数据库索引
add_index :tasks, :project_id
<强> 2。在明智的地方使用预先加载以避免n+1
查询问题。
Project.find(12).includes(:tasks, :notes)