体验1440数据库迁移



想象一下Oracle DBA。 他已经三十多岁了,他有点超重,穿一件背心,他有一个秘密的访问令牌可以挂在脖子上的所有底座上,并且摘要了他通过的认证的一半。 周六 大发布日。 高潮 是时候将更改发布到数据库了。 他输入sqlplus,按Enter,然后在黑屏上的空白处进入数以千计的SQL命令。 就像在星球大战中一样。 五分钟后,一切就绪。 一个小时后,发布完成。 工作完成了,当天取得了成功。 现在您可以喝几杯啤酒了。

另一件事是星期一。 事实证明,由于错误而未执行某些命令,但是这些命令并未使脚本不受限制地追求黑空。 领导层的一些压力使原本很难解决的任务变得复杂。 总体而言,星期一没有工作。

当然,这是一个虚构的故事。 这从来没有发生过。 至少,如果通过迁移组织了更改数据库架构的工作,那将不会发生。

什么是数据库迁移工具?


通过迁移管理数据库架构更改的想法非常简单:

  1. 每个更改都作为单独的迁移文件发布。
  2. 迁移文件包括直接和反向更改。
  3. 迁移到数据库的应用是由一个特殊的实用程序执行的。

最简单的迁移示例:
-- 20180618152059: create sequence for some_table CREATE SEQUENCE some_table_seq; --//@UNDO DROP SEQUENCE some_table_seq; 

与在公共SQL文件中组织更改相比,此方法具有许多优势。 仅仅没有合并冲突是值得的。

更令人惊讶的是,该方法本身最近才获得普及。 最初内置迁移工具的Ruby on Rails框架似乎是该方法的主要成名之处,即2005年底。 稍早些时候,Martin Fowler在2003年撰写了有关该方法的文章,可能的重点是,直到本世纪初,开发才开始积极适应版本控制系统的使用。 早在2000年, Joel Spolsky测试的第一段就是“您使用源代码控制吗?” -这表明当时并不是每个人都使用版本控制系统。 但是我们分心了。

MyBatis迁移八年


Wrike,我们于2010年3月29日凌晨12点半开始使用数据库更改方法进行迁移。 自那时以来,我们已经实施了1,440项迁移,其中包括6,436项直接更改和5,015项反向更改。 通常,我们已经将MyBatis迁移工具与PostgreSQL结合使用获得了一些经验。

简而言之,我们从未后悔。 如果碰巧您没有使用迁移或类似方法,那么该开始了。 是的, 奔腾4也已过时。

但是谈论任何事物的优点很无聊,让我们直接面对困难。

PostgreSQL的细节


也许为Postgres编写迁移没有任何困难,除了以下两个方面:
  • 您无法创建索引,
  • 您不能添加NOT NULL列。

不,实际上是有可能的,只是不是完全以明显的方式。 创建索引时,应始终指定CREATE INDEX CONCURRENTLY ,否则将中断生产,因为Postgres将在创建索引的过程中锁定表,这可能会花费很长时间。 当然,开发人员一旦忘记了它,就必须始终牢记这种微妙之处。 这里可以编写一个测试。 但这只是一个小麻烦。

创建NOT NULL列比较棘手,这里需要分四个步骤进行更改:
  1. 创建一个NULL列(在Postgres中是免费的)。
  2. 将DEFAULT列设置为一个值。
  3. 在循环中,以增量方式更新DEFAULT中的NULL值。
  4. 设置SET NOT NULL。

这里最大的收获是在第三段。 NULL值需要UPDATE some_table SET some_column='' WHERE some_column IS NULL更新,因为UPDATE some_table SET some_column='' WHERE some_column IS NULL ; 与索引一样,将阻塞表,结果相同。 而且迁移只能执行SQL命令,因此必须手动将此类脚本投入生产。 低于平均水平的快乐。 现在,如果可以在“迁移”中编写一个周期,那就没有问题了。 也许这是通过钩子实现的。

创建UNIQUE索引和更改PRIMARY KEY也需要一些技巧,但是这些操作很少涉及。

群集详细信息


只要您有一个数据库,数据库迁移管理工具就很好。 如果您有多个基础,那就更有趣了。 尤其是如果您有多种类型的数据库,每种类型都有多个实例。

结果,在进行git pull开发人员必须将更改滚动到第一个数据库的第一个实例,然后滚动到第二个实例,再滚动到第二个数据库的第一个实例,依此类推-这样的原理。 在这里编写用于管理数据库迁移管理实用程序的实用程序是正确的。 完全自动化。

角色杂耍


众所周知,作为实体的角色并不存在于单独的数据库级别上,而是存在于整个数据库服务器级别上,至少在Postgres中。 在这种情况下,您可能需要REVOKE INSERT ON some_table FROM some_role指定REVOKE INSERT ON some_table FROM some_role 。 仍然可以期望角色在生产中被预先配置,但是对于开发人员或登台来说,这已经很困难。 同时,在开发过程中,当然,所有数据库都位于同一台本地服务器上,因此您不能在迁移中编写CREATE ROLE ,并且不支持IF NOT EXISTS 。 一切都简单地解决了:
 DO $$ BEGIN IF NOT EXISTS (SELECT * FROM pg_roles WHERE rolname = 'some_role') THEN CREATE ROLE "some_role" NOLOGIN; END IF; END; $$; 

看吧! 我抓住并扔掉它们,抓住并扔掉,这很简单。

一点发展现实


开发人员会犯错误,甚至在SQL迁移中也会发生这种情况。 通常,可以在审阅中注意到错误,但也可能是异常的。 如果我们谈论直接的变化,那么那里的门框仍然无法生产-验证阶段太多。 但是随着相反的变化,可能会发生事故。 为了避免UNDO迁移中的错误,在测试迁移时,您不仅需要执行./migrate up迁移,还需要执行./migrate up ./migrate up ,然后./migrate down ,然后再次./migrate up 。 这没什么复杂的,您只需要确保40个开发人员总是这样做即可。 实用程序可以很好地为开发人员环境自动执行这样的组合。

测试环境


如果测试环境是短暂的:假设您创建了一个容器,初始化了数据库并运行了集成测试,那么应该没有任何问题。 我们先./migrate bootstrap ,然后再进行./migrate up ,您就完成了。 只有当迁移数量超过一千时,此过程才可能延迟。 当数据库初始化的时间比测试运行的时间长时,这是一种耻辱。 我们必须躲避。

在寿命长的环境中,这仍然很难。 质量检查,您知道,他们不希望上班时会看到无可挑剔的干净数据库。 我不知道为什么会这样,但事实是事实。 因此,必须完整地维护用于手动测试的基础状态。 这并不总是那么容易。

精妙之处在于,如果将迁移应用于数据库,则将迁移标识符写入数据库。 并且,如果稍后更改了迁移代码,数据库将不会受到影响。 如果更改不是很关键,则代码可以成功投入生产。 Rssynchron。 当然,这是一个耻辱。 使用迁移的首要原则是永远不要更改书面迁移,而总是创建新迁移。 但是有时候我会觉得有些困惑-我会在这里稍作改动,因为事实是事实,一切都不会破裂。 当然可以! 来吧!

如果在审查后签署了移徙协议,则有可能禁止将草稿申请分期进行。 而且不仅可以将迁移标识符保存在changelog ,而且还可以保存checksum -也很有用。

照原样返回


当任务被取消时,发生了一个特别阴险的转折:他们做了,做了并且改变了主意。 这是正常情况。 一旦不再需要该代码,则应删除该分支。 当时有移民...而且她已经在演出...啊,...哎呀。 检查是否可以还原存储库备份的一个很好的理由。 尽管回忆起也许更容易。

同时,迁移是文本。 并且可以将文本保存在changelog 。 然后,如果从代码的迁移消失了,无论是什么原因都没关系,它总是可以回滚的。 甚至自动。

再次撤消


肯定需要UNDO部分。 但是为什么要写呢? 当然,有一些引人注目的情况,但是大多数更改是CREATE TABLEADD COLUMNCREATE INDEX 。 对于他们来说,该实用程序可以直接使用SQL代码自动生成反向操作。 当然是有特殊性的。 CREATE TABLE ${name} -这是一个如此特殊的团队,突然变得不合标准。 是的,要生成DROP TABLE ${name} ,您需要能够解析该表达式直至第三个单词。 尽管总的来说,这是一项完全可行的技术任务。 可能是开箱即用的。

结论


当然,我发现了错误。 MyBatis Migrations被认为是一种简单而通用的实用程序,与数据库的具体情况之间的联系最少。 她不仅仅是为自己辩护。 但是似乎有一些小的改进将使它变得更好,尤其是在长距离使用时。
--
德米特里·马莫诺夫(Dmitry Mamonov)

Source: https://habr.com/ru/post/zh-CN414441/


All Articles