• linkedu视频
  • 平面设计
  • 电脑入门
  • 操作系统
  • 办公应用
  • 电脑硬件
  • 动画设计
  • 3D设计
  • 网页设计
  • CAD设计
  • 影音处理
  • 数据库
  • 程序设计
  • 认证考试
  • 信息管理
  • 信息安全
菜单
linkedu.com
  • 网页制作
  • 数据库
  • 程序设计
  • 操作系统
  • CMS教程
  • 游戏攻略
  • 脚本语言
  • 平面设计
  • 软件教程
  • 网络安全
  • 电脑知识
  • 服务器
  • 视频教程
  • vbs
  • DOS/BAT
  • hta/htc
  • python
  • perl
  • VBA
  • ColdFusion
  • ruby
  • PowerShell
  • Lua
  • Golang
  • linux shell
您的位置:首页 > 脚本语言 >ruby > 在Ruby on Rails中优化ActiveRecord的方法

在Ruby on Rails中优化ActiveRecord的方法

作者:goldensun 字体:[增加 减小] 来源:互联网

goldensun 通过本文主要向大家介绍了ruby on rails,ruby on rails教程,ruby on rails安装,ruby on rails下载,ruby on rails指南等相关知识,希望对您有所帮助,也希望大家支持linkedu.com www.linkedu.com

 Ruby on Rails 编程常常会将您宠坏。这一不断发展的框架会让您从其他框架的沉闷乏味中解脱出来。您可以用习以为常的几行代码片断表达自己的意图。而且还可以使用 ActiveRecord。

对于我这样的一个老 Java? 程序员而言,ActiveRecord 多少有点生疏。通过 Java 框架,我通常都会在独立的模型和模式之间构建一种映射。像这样的框架就是映射框架。通过 ActiveRecord,我只定义数据库模式:或者用 SQL 或者用称为迁移(migration)的 Ruby 类。将对象模型设计建立于数据库结构之上的那些框架称为包装框架。与大多数包装框架不同,Rails 能通过查询数据库表发现对象模型的特征。与构建复杂查询不同,我使用模型在 Ruby(而非 SQL)中遍历关系。这样一来,我既获得了包装框架的简单性,又具备了映射框架的大部分功能。ActiveRecord 易于使用和扩展。有时,甚至有些过于简单。

与任何数据库框架一样,ActiveRecord 让我极易做出很多惹麻烦的事。我所能获取的列太多,又很容易遗漏重要的结构化数据库特性,比如索引或空约束。我并不是说 ActiveRecord 是个不好的框架。只不过若是需要扩展,您需要知道如何坚固自己的应用程序。在本篇文章中,我将带您亲历在使用 Rails 这一独树一帜的持久性框架时可能需要的一些重要优化。
基础管理

生成受模式支持的模型异常容易,只需很少的代码,即 script/generate model model_name。正如您所知,该命令可生成模型、迁移、单元测试甚至一个默认的 fixture。在该迁移中填上一些数据列,并输入一些测试数据、编写几个测试、添加几个验证就算大功告成,这样做真是很有诱惑力。但请您三思而行。您应该考虑总体的数据库设计,要特别注意以下这些事情:

  •     Rails 不会让您摆脱基本的数据库性能问题。数据库需要信息,这些信息经常以索引的格式才能有不错的性能。
  •     Rails 不会让您摆脱数据完整性问题。虽然大多数 Rails 开发人员都不喜欢在数据库中保留限制,但您应该考虑像空列这样的事情。
  •     Rails 为很多元素提供了方便的默认属性。有时,像文本字段的长度这样的默认属性对于大多数实用的应用程序而言都会过大。
  •     Rails 不会强制您创建有效的数据库设计。

在您继续跋涉,深入学习 ActiveRecord 之前,应该首先确保您已经打好了足够的基础。请确保索引结构可以为您所用。如果给定的表很大,如果将在列上而不是 id 上搜索,如果索引能对您有所帮助(更多细节,请参见数据库管理器文档 —— 不同的数据库以不同方式使用索引),那么就需要创建索引。无需采用 SQL 创建索引 —— 可以简单地使用迁移创建。可以轻松地使用 create_table 迁移创建索引,也可以创建一个额外的迁移来创建索引。以下是一个迁移示例,可用来为 ChangingThePresent.org (请参见 参考资料)创建索引:
清单 1. 在迁移中创建索引

class AddIndexesToUsers < ActiveRecord::Migration
 def self.up
  add_index :members, :login
  add_index :members, :email
  add_index :members, :first_name
  add_index :members, :last_name
 end

 def self.down
  remove_index :members, :login
  remove_index :members, :email
  remove_index :members, :first_name
  remove_index :members, :last_name
 end
end

</div>

ActiveRecord 会负责 id 上的索引,我显式地添加了可在各种搜索中使用的索引,原因是此表很大、不经常更新却经常被搜索。通常,我们会等到对给定的查询中的问题有一定的把握后才会采取相应动作。这种策略可以让我们不必二次猜测数据库引擎。但从用户这方面来看,我们知道该表将会很快具有数百万的用户,如果在经常搜索的列上没有索引,该表的效率会很低。

另外两个常见问题也与迁移有关。如果字符串和列都不应该为空,那么就请确保正确编写了迁移。大多数 DBA(数据库管理员)都会认为 Rails 为空列提供了错误的默认属性:默认情况下列可以为空。如果希望创建一个不能为空的列,您必须显式地添加参数 :null => false。如果具有字符串列,请务必确保编写应用程序的限值。默认地,Rails 迁移会将 string 列按 varchar(255) 编码。通常,这个值过于庞大。应该尽量保持能如实反应应用程序的数据库结构。与提供无任何限制的 login 相反,如果应用程序限制 login 只能为 10 个字符,那么就应该相应地编写数据库,如清单 2 所示:
清单 2. 用限值和非空列编写迁移

t.column :login, :string, :limit => 10, :null => false

</div>

此外,还应该考虑默认值以及其他任何能安全提供的信息。通过一点预备工作,就可以节省日后跟踪数据完整性问题的大量时间。在考虑数据库基础的同时,还应该注意哪些页是静态且容易缓存的。在优化查询和缓存页面这两个选项当中,如果您能 “消受” 复杂性,缓存页面将会带来更大的回报。有时,页面或片段都是纯静态的,比如一列状态或一组经常问到的问题。在这种情况下,缓存更胜一筹。而在其他的一些时候,您可能会决定牺牲数据库性能,以减少复杂性。对于 ChangingThePresent,根据问题和环境的具体情况,我们二者都尝试了。如果您也决定要牺牲查询性能,就请继续阅读吧。
N+1 问题

默认情况下,ActiveRecord 关系十分懒散。这意味着框架会一直等待访问关系直到您实际访问了该关系。比方说,每个成员都会有一个地址。可以打开一个控制台并输入如下命令:member = Member.find 1。可以看到追加到日志的如下内容,如清单 3 所示:
清单 3. 从 Member.find(1) 登录

^[[4;35;1mMember Columns (0.006198)^[[0m  ^[[0mSHOW FIELDS FROM members^[[0m
^[[4;36;1mMember Load (0.002835)^[[0m  ^[[0;1mSELECT * FROM members WHERE
 (members.`id` = 1) ^[[0m

</div>

Member 具有到此地址的关系,并由宏 has_one :address, :as => :addressable, :dependent => :destroy 定义。注意当 ActiveRecord 加载了 Member 时,您并不会看到地址字段。但如果在控制台中键入 member.address,就可以在 development.log 中看到清单 4 中的内容:
清单 4. 访问关系会强制数据库访问

 ^[[36;2m./vendor/plugins/paginating_find/lib/paginating_find.rb:98:in `find'^[[0m
^[[4;35;1mAddress Load (0.252084)^[[0m  ^[[0mSELECT * FROM addresses WHERE
 (addresses.addressable_id = 1 AND addresses.addressable_type = 'Member') LIMIT 1^[[0m
 ^[[35;2m./vendor/plugins/paginating_find/lib/paginating_find.rb:98:in `find'^[[0m

</div>

所以 ActiveRecord 并不会为地址关系执行查询,直到您实际访问 member.address。通常,这种懒散设计会工作得很好,因为持久性框架无需移动如此多的数据来加载成员。但如果您想要访问很多成员以及所有成员的地址,如清单 5 所示:
清单 5. 用地址检索多个成员

Member.find([1,2,3]).each {|member| puts member.address.city}

</div>

由于您应该看到针对每个地址的查询,所以就性能而言,结果并不尽如人意。清单 6 给出了问题的全部:
清单 6. N+1 问题的查询

^[[4;36;1mMember Load (0.004063)^[[0m  ^[[0;1mSELECT * FROM members WHERE
 (members.`id` IN (1,2,3)) ^[[0m
 ^[[36;2m./vendor/plugins/paginating_find/lib/paginating_find.rb:98:in `find'^[[0m
^[[4;35;1mAddress Load (0.000989)^[[0m  ^[[0mSELECT * FROM addresses WHERE
 (addresses.addressable_id = 1 AND addresses.addressable_type = 'Member') LIMIT 1^[[0m
 ^[[35;2m./vendor/plugins/paginating_find/lib/paginating_find.rb:98:in `find'^[[0m
^[[4;36;1mAddress Columns (0.073840)^[[0m  ^[[0;1mSHOW FIELDS FROM addresses^[[0m
^[[4;35;1mAddress Load (0.002012)^[[0m  ^[[0mSELECT * FROM addresses WHERE
 (addresses.addressable_id = 2 AND addresses.addressable_type = 'Member') LIMIT 1^[[0m
 ^[[35;2m./vendor/plugins/paginating_find/lib/paginating_find.rb:98:in `find'^[[0m
^[[4;36;1mAddress Load (0.000792)^[[0m  ^[[0;1mSELECT * FROM addresses WHERE
 (addresses.addressable_id = 3 AND addresses.addressable_type = 'Member') LIMIT 1^[[0m
 ^[[36;2m./vendor/plugins/paginating_find/lib/paginating_find.rb:98:in `find'^[[0m

</div>

结果正如我所预见的那样糟糕。所有成员共用一个查询,而每个地址各用一个查询。我们检索了三个成员,所以一共用了四个查询。如果是 N 个成员,就会有 N+1 个查询。这就是可怕的 N+1 问题。大多数持久性框架都采用热关联(eager association)来解决该问题。Rails 也不例外。如果需要访问关系,就可以选择将其包括到初始查询中。ActiveRecord 使用 :include 选项来实现此目的。如果将查询更改为 Member.find([1,2,3], :include => :address).each {|member| puts member.address.city},结果就会稍好一些:
清单 7. 解决 N+1 问题

^[[4;35;1mMember Load Including Associations (0.004458)^[
  


 

您可能想查找下面的文章:

  • Rails bundle命令安装mysql gem包出错的解决方法
  • win7安装ruby on rails开发环境
  • 简单对比分析Ruby on Rails 和 Laravel
  • 在 Ubuntu 12.04 Server 上安装部署 Ruby on Rails 应用
  • 快速正确的安装 Ruby, Rails 运行环境
  • Windows下Ruby on Rails开发环境安装配置图文教程
  • Ruby On Rails中如何避免N+1问题
  • 艰难完成 nginx + puma 部署 rails 4的详细记录
  • 浅谈Rails 4 中Strong Parameters机制
  • Rails命令行常用操作命令简明总结

相关文章

  • 深入讲解Ruby中Block代码快的用法
  • Ruby中的迭代器详解
  • Ruby实现的最长公共子序列算法
  • Ruby入门点滴-Ruby的安装
  • Ruby on Rails所构建的应用程序基本目录结构总结
  • ruby线程实现生产者消费者问题示例(队列Queue实现线程同步)
  • 更改RubyGem安装源
  • Ruby 中关于日文转UTF-8及半角全角转换的技巧
  • Ruby优化继承类实例
  • Ruby最简单的消息服务器代码

文章分类

  • vbs
  • DOS/BAT
  • hta/htc
  • python
  • perl
  • VBA
  • ColdFusion
  • ruby
  • PowerShell
  • Lua
  • Golang
  • linux shell

最近更新的内容

    • Ruby实现的合并排序算法
    • Ruby简明教程之方法(Method)介绍
    • Ruby中的Proc类及Proc的类方法Proc.new的使用解析
    • Ruby中的数组和散列表的使用详解
    • Ruby中的p和puts的使用区别浅析
    • ruby开发的交互式程序例子
    • ruby 面向对象思维 概念
    • 在Ruby程序中连接数据库的详细教程
    • Ruby元编程小结
    • Ruby的安装与运行

关于我们 - 联系我们 - 免责声明 - 网站地图

©2020-2025 All Rights Reserved. linkedu.com 版权所有