这个PHP无解深坑,你能解出来吗?(据说能解出来的都很秀)

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

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

1. 问题背景

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

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

因而乎,PHP Laravel框架提供了db migration的功能,用代码来管理数据库。参考连接laravel

2. 问题描述

在一个新的版本中,我将本身的数据库变动用以下方式记录git

php artisan make:migration db_migration_for_new_version

这会在项目的database/migrations目录下建立一个新的PHP文件,本身填入要变动的数据库内容github

public function up {
    Schema::create('a_new_table', function(Blueprint $table) {
        $table->bigIncrements('id');
    });

    Schema::create('another_new_table', function(Blueprint $table) {
        $table->bigIncrements('id');
        $table->string('user', 64)->default(0)->comment('用户名');

        // 这里模拟出现错误的情形
        throw new \Exception("出现错误");
    });
}

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

php artisan migrate

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

由于刚才第一次执行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、GB23十二、GBK、GB18030编码
其实你不必定懂csv文件格式
【每日课程推荐】机器学习实战!快速入门在线广告业务及CTR相应知识

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

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

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

相关文章
相关标签/搜索