【发布时间】:2014-06-29 10:27:16
【问题描述】:
我正在构建一个 Rails 4.1 应用程序(使用 Postgres 作为我的数据库),其中有几个模型以下列方式设置:
class Components < ActiveRecord::Base
has_many :compositions
scope :abridged, -> { where(abridged: true) }
end
class Compositions < ActiveRecord::Base
belongs_to :foo
belongs_to :component
scope :abridged, -> { joins(:component).where(components: { abridged: true }) }
# Or alternatively, { joins(:component).merge(Component.abridged) }
end
总结一下:
Component 模型由 Compositions 连接模型引用 - 每个组合属于一个组件。这些数据是从外部 CSV 文件导入的。组件表有一个布尔值abridged 列,它定义了哪些组件是数据删节子集的一部分(总共 360 个组件中的大约 85 个组件)。我希望轻松访问属于这个删节子集的组成部分(400,000 个中的约 180,000 个组成部分),因此我声明了一个 Composition.abridged 命名范围,它依赖于与组件表的连接以检查 abridged相关组件的条件。
这可以正常工作,但是对于某些查询来说它非常慢。例如,如果我在我的控制器中对删节的组合数据进行分页,如下所示:
Composition.abridged.order(:foo_id).page(params[:page])
我得到一个这样的 SQL 查询:
SELECT compositions.* FROM compositions INNER JOIN components
ON components.id = compositions.component_id
WHERE components.abridged = 't'
ORDER BY compositions.foo_id ASC
LIMIT 20 OFFSET 185284
- 在我的开发虚拟机中平均需要大约 2000 毫秒,而对完整数据集进行等效查询大约需要 30 毫秒!
如果我删除 ORDER BY 子句,它会将其减少到 ~80 毫秒,这不是很有帮助,因为无法保证返回记录的顺序,但它确实表明我的索引可能有问题。但是,我在两个表上都尝试了单个/组合索引的所有可能组合,但没有任何改进。执行一些EXPLAIN 查询确认数据库根本没有使用索引。在考虑之后,我认为这是有道理的——数据库不能有效地利用索引,因为过滤条件在另一个表上。如果我删除了WHERE components.abridged = 't' 条件并在没有它的情况下进行连接,那么EXPLAIN 表明索引使用得很好并且查询非常快。
在寻找解决此问题的方法时,我遇到了materialized views。基本上,这解决了我的速度问题,因为它使用连接查询数据预填充了本质上是附加表的内容,因此该部分最初只需要执行一次。然而,这种方法在我的应用程序中引入了一些主要缺点 - 最重要的是它需要(据我所知)第二个模型,这反过来又需要一些变通方法以避免重复业务逻辑,正确关联,确保更改发生在原始表而不是在物化视图上尝试(不能直接更改),并且当某些内容发生更改时会刷新视图(它不会自动执行)等。如果有办法我可以只需告诉Compositions.abridged 范围在不使用额外模型的情况下切换表,那么这种方法可能是理想的。
所以我的问题是:有没有一种方法可以查询合成的删节子集,从而可以简单地使用基本范围而不会导致显着的速度损失?
我没有提到向compositions 表添加布尔列的可能性。我对这个想法持开放态度,但由于以下几个原因犹豫不决:
- 它正在复制数据。
- 最初需要使用正确的布尔值填充约 400,000 行(导入过程在我的 VM 上已经花费了一个多小时),然后在删节的组件发生变化时正确维护。
- 我听说数据库可能甚至不使用
abridged列上的索引,因为它占数据集的近一半。
欢迎提出任何建议。
【问题讨论】:
-
你试过了吗:
Composition.where(component_id: Component.abrigded.pluck(:id)).order(foo_id: :asc)?我认为这应该导致子查询。而且它应该只预先订购结果而不是整个集合。
标签: ruby-on-rails database postgresql activerecord ruby-on-rails-4