经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » 程序设计 » PHP » 查看文章
这个PHP无解深坑,你能解出来吗?(听说能解出来的都很秀)
来源:cnblogs  作者:腾讯云+社区  时间:2018/9/25 20:37:32  对本文有异议

欢迎大家前往腾讯云+社区,获取更多腾讯海量技术实践干货哦~

本文由horstxu发表于云+社区专栏

1. 问题背景

PHP Laravel框架中的db migration是比较常用的一个功能了。在每个版本迭代中,除了代码会变动之外,一般数据库的字段或者数据库表也会有些变动。因此在新版本上线时,除了发布新版代码,不可避免地要把数据库的变动也执行了。在没有db migration功能之前,我们的做法是把要变动库表的SQL语句写好(CREATE TABLEALTER TABLE等)存在一个sql文件中,然后在上线时连接数据库,将sql语句执行一遍。

这么做比较大的一个缺点是没有数据库的版本管理,万一上线失败,要回滚版本,还要把sql文件里的内容再写个反向的SQL(DROP TABLEDROP COLUMN等)。这种方式也比较原始,在web开发中,我们总是希望尽量避免开发直接用原始的sql来操作数据库,出错风险很高,并且很有可能出现不可逆的错误,每次操作都要提心吊胆。

于是乎,PHP Laravel框架提供了db migration的功能,用代码来管理数据库。参考链接

2. 问题描述

在一个新的版本中,我将自己的数据库变更用如下方式记录

  1. php artisan make:migration db_migration_for_new_version

这会在项目的database/migrations目录下创建一个新的PHP文件,自己填入要变更的数据库内容

  1. public function up {
  2. Schema::create('a_new_table', function(Blueprint $table) {
  3. $table->bigIncrements('id');
  4. });
  5. Schema::create('another_new_table', function(Blueprint $table) {
  6. $table->bigIncrements('id');
  7. $table->string('user', 64)->default(0)->comment('用户名');
  8. // 这里模拟出现错误的情形
  9. throw new \Exception("出现错误");
  10. });
  11. }

在上面这个例子中,我的本意是想要创建两个表格。然而在第一个表格创建完了以后,第二个表格出现错误导致创建失败了。按照正常流程,我在上线时应该执行如下指令创建表格

  1. php artisan migrate

由于第二个表格创建失败,这时候上面的指令必然会报错。然而报错之后你应该怎么做呢?首先当然是把代码里出现错误的地方修正,然后应该怎么搞?此时数据库里面第一个表已经建好了,第二个表还没建。这时候你如果再执行php artisan migrate会报错:你第一张表格已经创建,不可重复创建表格。你可能会感觉,我需要回滚一次,于是你可能会执行回滚操作php artisan migrate:rollback --step=1。这里需要强调,此时千万别回滚!!!

因为刚才第一次执行migration出错,导致数据库并没有生成一个新的版本号。这时候如果回滚,那你回滚的是上个版本发布的时候做执行的数据库操作,而不是你刚刚执行的这个版本的数据库操作,这很可能是灾难性的,会导致你数据丢失。目前数据库最新版本是什么,可以参考数据库中migrations表的batch字段(这个表是laravel migration功能自动生成和管理的,并非业务表)。

总结一下这一无解深坑: db migration进行到一半时出错,此时只能手动操作数据库把已经执行的操作回滚掉,无法再通过artisan指令进行回滚

3. 为什么无解?

其实GitHub和StackOverflow上有很多人已经碰到了这个问题,但是答案都很悲观。

所有人的第一反应都是:可以开启事务操作么?将一次migration的所有操作视为一个整体,要么都成功,要么都失败可以么?很遗憾,不支持事务操作。在mysql里面,只有进行update、insert、delete这些常规操作时才可以有事务,而我们migration中执行的都是DDL(Data Definition Language)操作。这种建表(CREATE TABLE)、修改表结构(ALTER TABLE)的操作是无法回滚的,即使开启了事务也无法回滚(参考链接)。把DDL操作放在一个事务(Transaction)中,会导致事务自动的提交(参考链接),这往往不是我们代码逻辑所期望的结果。

4. 那该怎么办?

如果你已经碰到了这种问题,那没办法只得手动去一条一条看数据库发生了什么变化,然后自己执行反向操作。

目前只能想到一些预防此问题出现的办法。根据GitHub上的开发者建议,最好每一个CREATE TABLEALTER TABLE操作都是一个单独的migration。即每次migration只建一张表,或只改一个表结构,只做一个操作( 参考链接)……

还有一种办法是,把自己的建表、改表操作都放在一个try catch结构中,一旦出现错误,直接调用migration文件中的down函数,把所做的操作回滚掉。不过这个需要注意up和down的兼容性。例如up中有ADD COLUMN操作,而down中有DROP COLUMN操作。在ADD COLLUMN操作执行之前就出错,直接取执行down函数中的DROP COLUMN,也会有可能报COLUMN不存在的错误。

总之,这个问题并没有十分完美的解决方案,堪称无解深坑,尤其要注意rollback操作不要乱做 ,不要为了弥补一个坑,给自己挖了更大的一个坑。

问答
PHP功能滥用?
相关阅读
一图弄懂ASCII、GB2312、GBK、GB18030编码
其实你不一定懂csv文件格式
【每日课程推荐】机器学习实战!快速入门在线广告业务及CTR相应知识

此文已由作者授权腾讯云+社区发布,更多原文请点击

搜索关注公众号「云加社区」,第一时间获取技术干货,关注后回复1024 送你一份技术课程大礼包!

海量技术实践经验,尽在云加社区

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号