Rails 缓存与数据库迁移:提升性能与管理数据库架构
1. 片段缓存
在 Rails 中,对于其他请求,若调用
stale?
或
fresh_when
方法,Rails 会为你生成必要的 HTTP 头。这两个方法都接受
:last_modified
时间戳(UTC 格式)和
:etag
。
etag
可以是响应所依赖的对象,也可以是这类对象的数组,这些对象需要能响应
cache_key
或
to_param
方法,ActiveRecord 会处理好这些。
1.1
stale?
和
fresh_when
方法的使用
-
stale?方法 :通常在涉及自定义渲染的if语句中使用,示例代码如下:
def show
@article = Article.find(params[:id])
if stale?(:etag=>@article, :last_modified=>@article.created_at.utc)
# ...
end
end
-
fresh_when方法 :在使用默认渲染时更方便,因为它会为你生成 304 Not Modified 响应,示例代码如下:
def show
fresh_when(:etag=>@article, :last_modified=>@article.created_at.utc)
end
1.2 页面片段缓存的作用
在动态网站中,缓存页面的部分内容非常有用。例如,博客应用可能会为每个用户定制问候语和侧边栏,这种情况下无法使用页面缓存,但文章列表在不同用户之间不会改变,就可以使用片段缓存。只需构建一次显示文章的 HTML,然后将其包含在提供给每个用户的定制页面中。
1.3 示例代码说明
1.3.1 控制器代码
class BlogController < ApplicationController
def list
@dynamic_content = Time.now.to_s
end
end
1.3.2 模拟的 Article 类
class Article
attr_reader :body
def initialize(body)
@body = body
end
def self.find_recent
[ new("It is now #{Time.now.to_s}"),
new("Today I had pizza"),
new("Yesterday I watched Spongebob"),
new("Did nothing on Saturday") ]
end
end
1.3.3 视图模板代码
<%= @dynamic_content %>
<!-- Here's dynamic content. -->
<% cache do %>
<!-- Here's the content we cache -->
<ul>
<% for article in Article.find_recent %>
<li><p><%= h(article.body) %></p></li>
<% end %>
</ul>
<% end %>
<!-- End of cached content -->
<%= @dynamic_content %>
<!-- More dynamic content. -->
这里的
cache
方法是关键,该方法关联的代码块内生成的所有输出都会被缓存。下次访问该页面时,动态内容仍会渲染,但代码块内的内容将直接从缓存中获取,不会重新生成。
1.4 避免将应用级代码放入视图模板的方法
可以让动作测试缓存片段是否存在,如果存在,动作就绕过昂贵的数据库操作,示例代码如下:
class Blog1Controller < ApplicationController
def list
@dynamic_content = Time.now.to_s
unless read_fragment(:action => 'list')
logger.info("Creating fragment")
@articles = Article.find_recent
end
end
end
视图模板代码如下:
<%= @dynamic_content %> <!-- Here's dynamic content. -->
<% cache do %>
<!-- Here's the content we cache -->
<ul>
<% for article in @articles %>
<li><p><%= h(article.body) %></p></li>
<% end %>
</ul>
<% end %>
<!-- End of the cached content -->
<%= @dynamic_content %> <!-- More dynamic content. -->
1.5 过期缓存片段
当文章更新时,缓存的文章列表版本会过时,需要使用
expire_fragment
方法使其过期。默认情况下,片段使用渲染页面的控制器和动作名称进行缓存。例如,要使文章列表的缓存片段过期,控制器可以调用:
expire_fragment(:controller => 'blog', :action => 'list')
如果页面上有多个片段,可以通过向
cache
方法添加参数(使用
url_for
约定)来覆盖片段的名称,示例代码如下:
<% cache(:action => 'list', :part => 'articles') do %>
<ul>
<% for article in @articles %>
<li><p><%= h(article.body) %></p></li>
<% end %>
</ul>
<% end %>
<% cache(:action => 'list', :part => 'counts') do %>
<p>
There are a total of <%= @article_count %> articles.
</p>
<% end %>
在控制器中,可以传递相同的参数给
expire_fragment
来删除特定的片段,示例代码如下:
class Blog2Controller < ApplicationController
def list
@dynamic_content = Time.now.to_s
@articles = Article.find_recent
@article_count = @articles.size
end
def edit
# do the article editing
expire_fragment(:action => 'list', :part => 'articles')
redirect_to(:action => 'list')
end
def delete
# do the deleting
expire_fragment(:action => 'list', :part => 'articles')
expire_fragment(:action => 'list', :part => 'counts')
redirect_to(:action => 'list')
end
end
expire_fragment
方法还可以接受一个正则表达式作为参数,用于使名称匹配的所有片段过期,示例代码如下:
expire_fragment(%r{/blog2/list.*})
1.6 缓存技术总结
通过学习,我们掌握了三种可使网站响应更快的技术:
- 缓存整个页面,避免处理可由缓存提供服务的请求时的所有 Ruby、Rails 和数据库开销,适用于高流量且很少更改的页面。
- 缓存控制器动作的结果,避免渲染和数据库开销,适用于需要运行过滤器(通常用于身份验证)的情况。
- 缓存页面片段,让我们能够完全控制优化和动态内容生成之间的平衡。
2. 数据库迁移
2.1 迁移的概念和作用
在 Rails 应用开发中,数据库架构会随着开发的进行不断演变,如添加表、重命名列等。Rails 通过迁移来实现这些步骤,迁移是应用程序
db/migrate
目录中的 Ruby 源文件。
2.2 创建和运行迁移
2.2.1 迁移文件的命名
每个迁移文件的名称以数字(通常是十四位)和下划线开头,这些数字是迁移的版本号,即创建迁移时的协调世界时(UTC)时间戳。例如,
20110211000001_create_products.rb
。
2.2.2 生成迁移文件的方法
-
模型生成器
:创建与模型关联的表的迁移(除非指定
--skip-migration选项),示例命令如下:
depot> rails generate model discount
会生成
db/migrate/20110211000010_create_discounts.rb
文件。
-
单独生成迁移
:示例命令如下:
depot> rails generate migration add_price_column
会生成
db/migrate/20110211000011_add_price_column.rb
文件。
2.2.3 运行迁移
使用
db:migrate
Rake 任务运行迁移,示例命令如下:
depot> rake db:migrate
运行迁移时,Rails 会维护一个名为
schema_migrations
的表,该表只有一个
version
列,每个成功应用的迁移都会在该表中添加一行。运行
rake db:migrate
时,任务会先查找
schema_migrations
表,如果不存在则创建,然后检查
db/migrate
目录中的所有迁移文件,跳过版本号已在数据库中的文件,应用其余的迁移。
2.2.4 强制数据库到特定版本
可以通过为
rake db:migrate
命令提供
VERSION=
参数来强制数据库到特定版本,示例命令如下:
depot> rake db:migrate VERSION=20110211000009
如果指定的版本号大于尚未应用的迁移版本号,这些迁移将被应用;如果小于
schema_migrations
表中列出的一个或多个版本号,Rails 会撤销相应的迁移,直到没有超过指定版本号的版本为止。
2.2.5 重新执行迁移
可以使用
rake db:migrate:redo
任务重新执行一个或多个迁移,默认情况下会回滚一个迁移并重新运行,要回滚多个迁移,可以传递
STEP=
参数,示例命令如下:
depot> rake db:migrate:redo STEP=3
2.3 迁移的结构
迁移是
ActiveRecord::Migration
类的子类,创建的类应至少包含
up
和
down
两个类方法,示例代码如下:
class SomeMeaningfulName < ActiveRecord::Migration
def self.up
# ...
end
def self.down
# ...
end
end
类名在所有大写字母转换为小写并加上下划线后,必须与文件名中版本号之后的部分匹配。
up
方法负责应用迁移的架构更改,
down
方法负责撤销这些更改。例如,添加
e_mail
列到
orders
表的迁移代码如下:
class AddEmailToOrders < ActiveRecord::Migration
def self.up
add_column :orders, :e_mail, :string
end
def self.down
remove_column :orders, :e_mail
end
end
2.4 列类型和选项
2.4.1 列类型
add_column
方法的第三个参数指定数据库列的类型,Rails 迁移使用逻辑类型来隔离底层数据库类型系统,不同数据库对同一逻辑类型的映射不同。支持的类型有
:binary
、
:boolean
、
:date
、
:datetime
、
:decimal
、
:float
、
:integer
、
:string
、
:text
、
:time
和
:timestamp
。
2.4.2 列选项
定义大多数列时可以指定最多三个选项,十进制列还可以额外指定两个选项,常见选项如下:
| 选项 | 说明 |
| ---- | ---- |
|
:null => true or false
| 如果为
false
,底层列会添加
not null
约束(如果数据库支持) |
|
:limit => size
| 设置字段大小的限制,会在数据库列类型定义后追加
(size)
|
|
:default => value
| 设置列的默认值,默认值在运行迁移时计算一次 |
|
:precision
| 对于十进制列,指定要存储的有效数字位数 |
|
:scale
| 对于十进制列,确定小数点在这些数字中的位置 |
示例列定义代码如下:
add_column :orders, :attn, :string, :limit => 100
add_column :orders, :order_type, :integer
add_column :orders, :ship_class, :string, :null => false, :default => 'priority'
add_column :orders, :amount, :decimal, :precision => 8, :scale => 2
2.5 重命名和更改列
2.5.1 重命名列
可以使用
rename_column
方法重命名数据库列,示例代码如下:
class RenameEmailColumn < ActiveRecord::Migration
def self.up
rename_column :orders, :e_mail, :customer_email
end
def self.down
rename_column :orders, :customer_email, :e_mail
end
end
重命名不会破坏与列关联的现有数据,但并非所有适配器都支持重命名。
2.5.2 更改列
使用
change_column
方法更改列的类型或选项,示例代码如下:
def self.up
change_column :orders, :order_type, :string
end
但从字符串列转换为整数列可能会有问题,例如下面的代码可能会有问题:
def self.down
change_column :orders, :order_type, :integer
end
通过以上内容,我们了解了 Rails 中的片段缓存和数据库迁移的相关知识,这些技术可以帮助我们提升应用的性能和管理数据库架构。
2.6 迁移操作流程图
下面是一个 mermaid 格式的流程图,展示了迁移操作的主要流程:
graph LR
A[开始] --> B[运行 rake db:migrate]
B --> C{schema_migrations 表存在?}
C -- 否 --> D[创建 schema_migrations 表]
C -- 是 --> E[检查 db/migrate 目录中的迁移文件]
D --> E
E --> F{版本号是否已在数据库中?}
F -- 是 --> G[跳过该迁移文件]
F -- 否 --> H[应用迁移]
H --> I[在 schema_migrations 表中添加一行]
G --> J{是否还有迁移文件?}
I --> J
J -- 是 --> E
J -- 否 --> K[结束]
2.7 迁移的注意事项
- 迁移顺序 :在多用户使用版本控制系统存储迁移文件时,可能会出现迁移文件版本号顺序不一致的情况。此时运行迁移可能会导致迁移顺序混乱,因此需要确保迁移之间相互独立,或者将数据库回滚到之前的状态,然后按顺序应用迁移。
- 数据完整性 :在进行列的重命名和更改操作时,要特别注意数据的完整性。例如,重命名列时虽然不会破坏现有数据,但某些数据库适配器可能不支持该操作;更改列类型时,可能会出现数据转换问题,如从字符串列转换为整数列时,非数字字符串可能会导致错误。
3. 总结与应用建议
3.1 缓存技术总结
- 页面缓存 :适用于高流量且很少更改的页面,能显著减少 Ruby、Rails 和数据库的开销,提高响应速度。例如,电商网站的产品目录页面,由于产品信息更新频率较低,可以使用页面缓存。
- 动作缓存 :在需要运行过滤器(通常用于身份验证)的情况下,缓存控制器动作的结果,避免渲染和数据库开销。比如,用户登录后的个人信息页面,每次访问都需要验证用户身份,可以使用动作缓存。
- 片段缓存 :提供了对优化和动态内容生成的精细控制。对于动态网站中部分内容变化频繁,而部分内容相对稳定的情况,如博客文章列表和动态问候语的组合,使用片段缓存可以在保证页面动态性的同时提高性能。
3.2 数据库迁移总结
- 迁移的灵活性 :Rails 的迁移机制允许开发者在开发过程中灵活地修改数据库架构,如添加表、重命名列等,适应不断变化的需求。
-
版本控制
:迁移文件的版本号和
schema_migrations表的使用,确保了数据库架构的版本控制,方便团队协作和项目的维护。 - 数据处理 :在进行迁移操作时,要充分考虑数据的完整性和兼容性,避免因迁移操作导致数据丢失或错误。
3.3 应用建议
- 缓存策略 :根据页面的特点和访问频率,合理选择缓存技术。对于静态页面或变化较少的页面,优先使用页面缓存;对于需要身份验证的页面,考虑使用动作缓存;对于页面中部分动态内容和静态内容混合的情况,使用片段缓存。
- 迁移管理 :在开发过程中,及时创建迁移文件来记录数据库架构的变化。在团队协作时,定期同步迁移文件,确保数据库架构的一致性。同时,在进行复杂的迁移操作前,先在测试环境中进行测试,避免对生产环境造成影响。
通过合理运用 Rails 的缓存技术和数据库迁移机制,可以有效提升应用的性能和可维护性,为用户提供更流畅的体验。在实际开发中,要根据具体需求和场景,灵活选择和组合这些技术,不断优化应用的性能和数据库架构。
超级会员免费看
12

被折叠的 条评论
为什么被折叠?



