清除旧的Rails迁移文件是一个好主意吗?

32

我已经运行一个大型Rails应用程序超过2年了,逐渐地,我的ActiveRecord迁移文件夹一直在增长,现在已经有超过150个文件。

有些非常老的模型,在应用程序中已不再使用,但是仍然被迁移文件所引用。我在考虑将它们删除。

你认为呢?你通常会从代码库中清除旧的迁移文件吗?

10个回答

18

The Rails 4 Way 第177页: Sebastian说...

一个鲜为人知的事实是,您可以删除旧的迁移文件(同时保留较新的)以保持 db/migrate 文件夹的可管理性。您可以将旧的迁移移动到 db/archived_migrations 文件夹或类似的位置。一旦裁剪了您的迁移文件夹的大小,使用 rake db:reset 任务从 db/schema.rb 创建(重新创建)数据库,并将种子加载到当前环境中。


1
这是每个人都想要阅读的答案。 - cisco

14

一旦我发布了一个重大的网站更新,我将把迁移合并为一个,并重新开始。当迁移版本号接近75时,我会感到不舒服。


7
如何将它们合并成一个?需要手动操作吗? - Alison R.
4
不确定您提问时的情况,但在Rails 3中,您可以直接复制 schema.rb - Nathan Long
1
为什么要将它们合并在一起?无论如何,schema.rb 应该是您数据库的权威来源。您可以运行 rake db:schema:load 获取最新的架构,然后运行 rake db:migrate 获取最新的迁移。 - lobati

5

个人而言,我喜欢在迁移文件中保持整洁。我认为一旦您将所有更改推送到生产环境后,您应该考虑归档迁移。唯一困难的是当 Travis 运行时它会运行 db:migrate,所以这些是我使用的步骤:

  1. 将历史迁移从 /db/migrate/ 移动到 /db/archive/release-x.y/

  2. 手动创建一个新的迁移文件,并使用 /db/archive/release-x.y 目录中上一次运行迁移的版本号,并将描述更改为类似于 from_previous_version 的内容。使用旧版本号意味着它不会在生产机器上运行并出现问题。

  3. ActiveRecord::Schema.define(version: 20141010044951) do 部分内的 schema.rb 内容复制并粘贴到 from_previous_version 变更日志的 change 方法中

  4. 全部检查完毕,Robert 就成了你父亲的兄弟。

唯一需要考虑的其他问题是您的迁移是否会创建任何数据(我的测试方案包含所有自己的数据,因此我没有这个问题)


1
Travis 和 Robert 是谁? - user12121234
2
Travis: https://travis-ci.org/ 而Bobs your uncle: https://en.wikipedia.org/wiki/Bob%27s_your_uncle - FuzzyJulz

5

我偶尔会清除所有已在生产中应用的迁移,对此我有至少 2 个原因:

  • 更易管理的文件夹:更容易发现新的迁移。
  • 更干净的文本搜索结果:项目内全局文本搜索不会导致大量无用匹配,因为某个三年前的迁移添加或删除了某些列,而这些列在任何情况下都不存在了。

4

1
我曾尝试过在没有模型引用的情况下编写迁移,结果证明这是一个相当大的头疼问题,特别是当我试图添加新的数据库约束时。我必须先编写一个rake任务来清理数据,然后再推送一个迁移以添加约束,因为在存在未运行的迁移时无法运行rake任务。把所有内容都放在迁移中要容易得多。额外的好处是它隐式地具有事务性,因此失败会回滚。 - lobati
@lobati:我不明白在迁移中使用模型和新数据库约束之间的连接。我在很多地方都使用真实的外键、检查、触发器等,使用 SQL 在迁移中管理它们,迁移中不需要模型。 - mu is too short
@muistooshort 我觉得当添加限制或移动数据时,能够依赖模型级别的验证和查询简便性非常好。例如,在添加外键约束时,有时会发现关联记录丢失,因此我们可能希望以某种方式有条件地重构它或仅销毁它。这些大部分可能可以通过原始SQL处理,但我也发现,如果我打错查询,拥有额外的抽象层是很好的。 - lobati
@lobati:我倾向于将数据库视为一个完全独立的应用程序,其API是SQL。这使得数据库负责其一致性,而所有模型验证都是备份(用于一致性和完整性),使得向外界通信错误变得更加容易。这会导致一些工作重复,但实际上这是一个特点。至于打错字,这就是为什么要将约束放在数据库中的原因。损坏的代码是暂时的,损坏的数据是永久的。我们可能在任何情况下都在谈论同样的事情。 - mu is too short
@muistooshort 我想我同意这一切。不幸的是,我并不总是在这些事情上那么明智,经常会将旧的工作与我的改变哲学或他人的现有代码库和数据库更新。顺便说一句:如果Rails能够从您的数据库约束推断验证,那就太方便了。 - lobati

3
为什么要删除迁移文件?除非磁盘空间出现问题,否则我认为没有好的理由来删除它们。我猜如果您绝对确定不会再回滚任何内容,那么您可以这样做。然而,似乎为了保存几KB的磁盘空间而这样做并不值得。此外,如果您只想删除引用旧模型的迁移,则必须手动查看所有迁移文件以确保不会删除应用程序中仍在使用的任何内容。对我来说,这需要付出很多努力,但收益却很少。

主要原因是因为/models文件夹中没有指向表的模型定义,因为该模型已被完全删除。 - Simone Carletti
在创建然后删除表的迁移中,这似乎是低效的,除非进行完整的数据库构建是一个时间关键的过程,否则没有理由去处理它们。它们喜欢逗留。 - Jeremy
2
如果您有非常多的迁移文件,这样做确实很有意义,我们已经有了大约1000个迁移文件,现在运行“rake db:migrate”需要花费很长时间——即使只有一个迁移需要运行,因为(我认为)Rails会首先将所有迁移文件加载(解析)到内存中。我们从来没有清除过我们的迁移文件,但现在我在这里,我一定会考虑这样做。 - Alex Kovshovik
1
一个原因是:我刚从应用程序中移除了Devise(转移到单点登录)。它的原始迁移使用了像t.recoverable这样的东西,如果没有这个gem就不存在,所以我根本无法运行那个迁移。至少我得把它注释掉。 - Nathan Long

3
查看http://edgeguides.rubyonrails.org/active_record_migrations.html#schema-dumping-and-you 迁移不是数据库的表示,结构.sql或schema.rb才是。迁移也不是设置/初始化数据的好地方。db/seeds或rake任务更适合执行此类任务。
那么迁移是什么呢?在我看来,它们是如何更改数据库模式的说明 - 正向或反向(通过回滚)。除非存在问题,否则应仅在以下情况下运行:
1. 在我的本地开发机器上作为测试迁移本身并编写模式/结构文件的方式。 2. 在同事开发人员机器上作为更改模式而不会删除数据库的方式。 3. 在生产机器上作为更改模式而不会删除数据库的方式。
一旦运行,它们就应该无关紧要。当然,错误可能发生,因此您绝对需要保留几个月的迁移以备回滚需要。
CI环境从不需要运行迁移。它会减慢CI环境并且容易出错(就像Rails指南中说的一样)。由于您的测试环境只有短暂的数据,因此应使用rake db:setup,它将从schema.rb / structure.sql加载并完全忽略您的迁移文件。
如果您使用源代码控制,则保留旧迁移没有好处。它们是源历史的一部分。如果这是您喜欢的,将它们放在存档文件夹中可能有意义。
尽管如此,我强烈认为清除旧的迁移是有道理的,原因如下:
1.它们可能包含太旧而无法运行的代码(例如,如果您删除了一个模型)。这会给想运行rake db:migrate的其他开发人员带来麻烦。 2.他们会减慢grep之类的任务,并且在某个年龄之后就不相关了。
为什么它们不相关呢?再说两个原因:历史记录存储在源代码控制中,实际的数据库结构存储在structure.sql / schema.rb中。我的经验法则是,12个月以前的迁移完全无关紧要。我会删除它们。如果有某种原因,我想回滚比那更老的迁移,我有信心数据库已经在那段时间内发生了足够的变化,以保证编写一个新的迁移来执行该任务。
那么,您如何摆脱迁移?以下是我遵循的步骤:
  1. 删除迁移文件
  2. 编写一个rake任务,用于删除数据库schema_migrations表中对应的行。
  3. 运行rake db:migrate重新生成structure.sql/schema.rb。
  4. 验证structure.sql/schema.rb中唯一更改的内容是删除了每个已删除迁移的行。
  5. 部署后,在生产环境上运行第2步中的rake任务。
  6. 确保其他开发人员在他们的机器上运行第2步中的rake任务。

第二个步骤是必要的,以保持schema/structure的准确性,这也是实际上唯一重要的事情。


1
一旦你确信旧的迁移不再需要,删除它们是可以的。迁移的目的是为了提供一个工具来进行数据库更改和回滚。一旦更改已经完成并在生产环境中运行了几个月,你很可能不会再需要它们。我发现,过了一段时间后,它们只是杂物,会使你的代码库、搜索和文件导航变得混乱。
有些人会从头开始运行迁移以重新加载他们的开发数据库,但这并不是它们的预期用途。你可以使用 rake db:schema:load 来加载最新的模式,并使用 rake db:seed 填充种子数据。 rake db:reset 会为您完成这两个操作。如果你有不能转储到 schema.rb 的数据库扩展,那么你可以使用 ActiveRecord 的 SQL 模式格式 并运行 rake db:structure:load

0

是的。我猜如果你已经完全从数据库中删除了任何模型和相关表,那么把它放在迁移中是值得的。如果迁移中的模型引用不依赖于任何其他东西,那么你可以删除它。虽然该迁移永远不会再次运行,因为它已经运行过了,即使你不从现有迁移中删除它,每当你重新迁移数据库时,它都会导致问题。

所以最好从迁移中删除该引用。并在大版本发布到生产数据库之前将迁移重构/最小化为一个或两个文件。


0

我同意,100多个迁移没有任何价值,历史记录一团糟,很难在单个表上跟踪历史记录,还会使文件查找变得混乱。在我看来,这只是浪费。

以下是将所有迁移压缩为与生产环境相同模式的三步指南:

第一步:从生产环境获取模式

# launch rails console in production
stream = StringIO.new
ActiveRecord::SchemaDumper.dump(ActiveRecord::Base.connection, stream); nil
stream.rewind
puts stream.read

这段内容与迁移相关,可以复制粘贴到迁移中,但需要去掉明显的标题。

第二步:在不运行生产环境的情况下进行迁移

这一点非常重要。使用最后一个迁移并更改其名称和内容。ActiveRecord将日期时间数字存储在其schema_migrations表中,以便知道它已经运行了什么而没有运行什么。重用上一个迁移,它会认为它已经运行过了。

例如:rename 20161202212203_this_is_the_last_migration -> 20161202212203_schema_of_20161203.rb

然后把模式放在那里。

第三步:验证和故障排除

在本地,运行rake db:drop、rake db:create、rake db:migrate

验证模式是否相同。我们遇到的一个问题是模式中的datetime "now()",以下是我能找到的最佳解决方案:https://dev59.com/_10Z5IYBdhLWcg3w3DQW#40840867


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接