停止杀死你的数据库:5 个 ActiveRecord 提升 Rails 应用速度的技巧

发布: (2026年2月22日 GMT+8 08:19)
4 分钟阅读
原文: Dev.to

Source: Dev.to

在本文中,我分享了自己使用 ActiveRecord 的历程。刚开始接触 Rails 时,我以为 ActiveRecord 只是把数据“魔法”般保存到数据库的工具。随着参与更大的项目,我意识到它的功能远不止这些。写出能正常工作的代码很容易,但写出让应用变慢的代码也同样容易。下面列出了五个层次的 ActiveRecord 知识,从简单查询到更高级的优化。

LEVEL 1: The Basics (Chaining)

大多数初学者只知道 findwhere。ActiveRecord 的酷点在于,你可以无限链式调用这些方法,直到真正请求数据时才会执行查询。

# Instead of doing this
users = User.where(active: true)
recent_users = users.where('created_at > ?', 1.day.ago)

# You can chain it nicely
User.where(active: true).where('created_at > ?', 1.day.ago)

这只会生成 一条 SQL 查询。Rails 会等到最后一刻才去触碰数据库。

LEVEL 2: Cleaning up with Scopes

在多个控制器里重复相同的 where 条件非常烦人。可以在模型中定义 scopes;它们的行为类似类方法。

# app/models/post.rb
class Post  { where(published: true, deleted: false) }
  scope :recent, -> { where('created_at > ?', 1.week.ago) }
end

现在控制器代码就干净多了:

# app/controllers/posts_controller.rb
def index
  @posts = Post.live.recent
end

LEVEL 3: Solving the N+1 Problem (Advanced)

最常见的错误是先加载父记录,然后在循环中遍历其子记录,导致每个关联都产生一次额外查询。

# controller
@posts = Post.all

# view (ERB)

Rails 会为帖子执行 1 条查询,再为每篇帖子的用户执行 N 条额外查询(每篇帖子一次)。如果有 100 篇帖子,就会产生 101 条查询,严重拖慢性能。

STEP 1: Use includes

告诉 ActiveRecord 在一次查询中把关联记录一起取回。

@posts = Post.includes(:user).all

Rails 现在只会执行 2 条 查询:

  1. 获取所有帖子。
  2. 获取这些帖子关联的所有用户。

LEVEL 4: Pluck vs Map (Optimization)

当只需要单列数据时,避免加载完整的 ActiveRecord 对象。

# Heavy: loads all User objects into memory
User.all.map(&:email)

改用 pluck 直接从数据库取出列:

# Fast: fetches only the email column
User.pluck(:email)

pluck 返回字符串数组,不会实例化模型对象,从而节省内存和时间。

LEVEL 5: Use exists? instead of present?

如果只需要判断记录是否存在,别去加载整条记录。

# Bad: loads the post into memory just to check existence
if Post.where(id: params[:id]).present?
  # …
end

# Good: runs a lightweight SQL `SELECT 1` with LIMIT 1
if Post.exists?(params[:id])
  # …
end

exists? 返回布尔值,并在找到匹配项后立即停止查询。


掌握这些小技巧后,你的 Rails 应用会更快,代码也会更简洁。

0 浏览
Back to Blog

相关文章

阅读更多 »

停止在 Rails 中错误使用 .any?

介绍:传递给 .any? 的单个块可能会在不发出警告或错误的情况下,悄悄将成千上万条记录加载到内存中——仅仅是产生不必要的对象。大多数 Rails 开发者……

简化 Rails 中的时间戳切换

概述:我经常使用时间戳(例如 completed_at)作为布尔标志。它比真正的布尔值提供更多的元数据,但在 UI 上你通常希望…