我有以下模型方案:
酒店(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/