
在PHP的世界中,数据库结构迁移工具是众所周知的
-Doctrine ,CakePHP的
Phinx ,
Laravel的Yii-这是我想到的第一件事。 当然,还有十几个。 它们中的大多数都与迁移一起使用-用于对数据库架构进行增量更改的命令。
我不会描述为什么,在哈布雷(Habré)上有很多关于该主题的文章。 例如:
此外,随着团队
经验的发展,我在不同分支机构的数据库结构不断变化。
原始SQL vs PHP API
我们用纯SQL编写迁移。 许多工具提供PHP api来编写翻译成SQL代码的指令。 现在我不明白为什么会这样? 这样的工具将始终受到其功能的限制。 它们不允许为特定引擎编写特定指令;您仍然必须使用纯SQL。 我不是在谈论编写程序和视图。
有人抱怨他不想学习ALTER命令的语法。。。嗯,我不知道,我打开了目录并写了这座山的例子,特别是在一个大型项目中。
数据迁移(INSERT,UPDATE)也总是用SQL编写。 因为您永远不能依赖ORM和模型的当前版本。 在一个版本中,它们不再是。
例如:
Rollback Country::delete()->where(....)->execute();
要回滚数据库的状态。 这个PHP类不再在仓库中。 您需要查找他所在的最后一次提交,然后从那里回滚。 Brrr ...
因此,SQL简单可靠:
DDL中的交易
过渡到PostgreSQL时,我忘记了像噩梦一样的中断迁移-迁移发生在中间,有些东西卷起来,有些东西不在那儿,坐下来编辑笔...这迫使我们编写原子的单行命令并一次运行一个。 交易一切都很简单:如果发生故障-一切都会回滚(嗯,几乎所有东西)))。 只需修复并重新启动即可。 自动装配会产生巨大的冲击,如果有东西掉落,它会迅速修复并上升。
视图(视图)和功能
这里的问题是它们不能像表中的ALTER那样进行增量更新。 需要DROP和CREATE。 即 关于差异(迁移文本),最终结果到底是什么还不清楚。 尤其是当逻辑扭曲时,这非常不便。 例如:
这里发生了什么变化?
我们停止了这样一个事实,即迁移旁边是一个爸爸,其中存储了当前视图和过程代码,该代码在回滚迁移中进行了更新和复制。
现在,差异变得像:

回到Avito,我们为
版本存储过程代码提供了一个有趣的解决方案
。通常,这种情况提出了一个很好的问题-如何查看数据库结构的特定对象中的更改历史记录。 对于每个表,我想查看与特定任务解决方案相关的更改历史记录。

在Habré上发现了一种有趣的
方法来自动修复数据库结构中的更改。
与分支机构合作
我永恒的痛苦是如何在两个A分支和B分支之间切换,每个分支都对数据库的结构进行了编辑。

有必要回滚A分支中的迁移(我们还必须记住哪些迁移和迁移多少),然后切换到B分支并回滚新迁移。 好吧,如果我们的编辑兼容,我可以切换到第二个分支,并从B滚动其他迁移。
如果没有呢? 如果我有多个这样的分支机构? 然后回滚所有这些审查状态? 我一直讨厌它...
现在,当切换到其他人的分支机构时,我可以自动删除其他人的迁移并滚动当前迁移:

其中:
D-在A分支中启动的A迁移,但它们不在当前分支中,建议删除它们
A-出现在新分支中且需要滚动的B迁移
在一个基座上进行测试和自动组装时,它变得极为方便。 没有任何意义或机会让每个分支机构从头开始创建基础。 切换到分支并自动同步数据库状态。
编号和执行顺序
我所知道的所有工具都是按时间标记迁移的,这是一个很好的解决方案。 如果我编写了几次迁移,则保留了必要的顺序。 另一个开发人员可以在另一个线程中拥有任何日期,甚至可以是我的-在我们与他一起滚动的顺序中都没有关系,我们的更改彼此独立。 即使我们使用相同的表(按列添加),所有必要的更改都将以任何顺序进行。 最主要的是要尊重我的依存编辑的顺序。

我不考虑需要编辑相同内容的情况-这些观点始终是一致的。 好吧,否则在组装和测试阶段会失败。
这是一个有趣的例子。
我们在一个视图或过程中进行不同的编辑,即 在那些通过删除而更新的结构中。 即 例如,我在视图中添加了col_A列,并向同事添加了col_B。 因此,如果他的代码在我的发布之后推出,那么他的专栏将没有我的专栏:
CREATE VIEW vusers AS SELECT login, name,
A分行 | B分行 |
---|
DROP VIEW vusers; CREATE VIEW vusers AS SELECT login, name, col_A, | DROP VIEW vusers; CREATE VIEW vusers AS SELECT login, name, col_B, |
在这种情况下,必须使一个分支依赖于另一个。
另一个有趣的情况是迁移中的更正。
最重要的是,无论您对其进行了多少更改,都将不再再次应用已应用的迁移(您需要先回滚,然后再次应用它)。 即 您发送了Migration用于测试,所有规则,然后您意识到了这一点并进行了少量编辑。 但是测试或您使用它的其他服务器对此一无所知。
在这些情况下,我们重命名迁移文件,添加新的版本号,以便迁移器开始将其解释为2个命令-回滚1和回滚2,
例如:

回滚
即使无法将基础恢复到原始状态,也请始终编写ROLLBACK。 例如,DROP TABLE,它可以是哪种ROLLBACK?
在这种情况下,我们编写一个空的CREATE TABLE。 最重要的是,开发系统始终可以在分支之间轻松切换。 对于PROD,已经在不同级别上确定了不可逆的修订管理。 我可以复制表,也可以重命名表而不是删除表。 但是编写迁移的原则-必须进行回滚,以将基础的结构恢复到初始级别,并且数据已经可以实现。
在战斗环境中,我一生只使用了1-2次回滚。 而且一直在开发中。 因此,我总是检查回滚是否将所有内容返回到所需状态。
开发人员经常会在回滚中犯错误。 因为 他们主要专注于新编辑,经过测试并与他们合作。 其他人员和流程已经在进行回滚。 因此,我始终将迁移测试为UP-ROLLBACK-UP
一个有趣的问题出现在永久测试基础上(未删除数据库)。 他们编写了一个迁移文件,回滚工作正常,将其发送给测试,测试人员以新格式生成了数据,尝试回滚,但他们没有提供新数据。 经典例子
ALTER TABLE abc ALTER COLUMN code SET NULL
太好了! 经过测试,数据库中充满了NULL值。 回滚:
ALTER TABLE abc ALTER COLUMN code SET NOT NULL
反之亦然:-(
您需要添加命令:
DELETE FROM abc WHERE code IS NULL
困难在于,如果我们不打算每次都从头开始重新创建数据库,则需要牢记这一点,而不要使其自动化。
关于数据删除的一点
通常,我们尝试不立即删除填充的表和列。 最好重命名或创建副本,然后在所有内容都固定下来并且数据失去相关性之后再删除它:
ALTER TABLE user_logs RENAME TO user_logs_20190223;
迁移者
我们现在正在与Laravel合作-他有一个标准的,熟悉的迁移管理引擎。 如果需要,即使它仍在PHP类中,也可以用纯SQL编写。 但是我反复尝试使其以我们需要的方式工作,导致产生了单独的回购:
- 该解决方案包括两部分-lib和特定控制台的实现(Laravel,Symfony)。 您可以集成到任何控制台中,或者至少集成到网络枪口中。
- 没有配置和连接-为什么,当它已经在您的项目中时。 紧扣您与接口的连接,然后开始。
- SQL回滚存储在数据库中。 这是在分支之间切换所必需的。
- 在Postgesql,Mysql上测试(无事务)。 原则上,它适用于任何基础和结构,因为使用了原始格式。
参考文献
-
迁移库-
在Laravel / Artisan下实施-
在Symfony /控制台下实施