laravel migration实践(踩坑经历)

7 篇文章 0 订阅

背景

数据库迁移也是laravel强大的功能之一,但是一直也只是在文档上看过,实际项目中还没有使用过,因为需要迁移的场景比较少,而且需要迁移的时候,直接mysqldump也很方便,好像不是很有必要。
但是最近遇到一个问题,开发不能登机器操作数据库了,这就比较难受了,一般发布新版本,都会有一些数据库变更的操作,常见的有:加表,加字段,加索引,初始化数据,修历史数据。当然改字段一般是不会改,除非特殊情况,改表就更不可能了,不能登机器怎么操作啊?写个脚本跑一下?但是总不能每次发布都加个脚本吧?这里就想到了migration好像有这种功能,所以这里在本地实践了一下:

环境

php:7.0
lumen:5.3
mysql:5.6
windows:10

migration的指令

windows上findstr类似于linux上的grep
在这里插入图片描述

php artisan migrate:install 新建一个迁移仓库,会在数据库中生成一个表migrations,第一次初始化的时候执行
表结构如下:
CREATE TABLE `migrations` (
	`id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
	`migration` VARCHAR(255) NOT NULL COLLATE 'utf8_general_ci',
	`batch` INT(11) NOT NULL,
	PRIMARY KEY (`id`)
)
COLLATE='utf8_general_ci'
ENGINE=InnoDB;


php artisan make:migration {migration_name} {--create=table_name} {--table=table_name} {--path=file_path}  新建一个迁移文件到file_path(使用默认的就好)目录,
文件名格式:date('Y_m_d_His')_{migration_name}.php
类名为:migration_name按_或者-分割后首字母大写,migration_name不可重复
--table:指定操作的表名,修改表使用这个参数
--create:指定创建的表名,新增表使用这个参数
--path:使用默认的路径database/migrations,不用传此参数

php artisan migrate {--step} {--pretend} {--force} 运行数据库迁移,按文件前面的时间,从小到大执行,如果正常执行结束,在migrations表中新增记录,batch值递增
--step:若有此参数,每一个文件batch会递增一次,回滚的时候可以每次回滚一个文件,如果没有此参数,一次执行多个文件会使用同一个batch值,只能多个文件一起回滚
--pretend:加了这个参数,不会真正执行迁移,会输出迁移需要执行的sql,参数没有绑定,会输出【?】
--force:production环境下强制执行(如果没有带该参数,会询问是否强制执行)

php artisan migrate:status 显示每条迁移的状态(是否已经迁移)


php artisan migrate:refresh {--step} {--force} 会执行所有迁移文件的回滚操作(可以指定回滚的step),然后执行迁移操作(指定step的话,只会执行回滚的那几步,否则全部执行一遍)
--step:回滚几步,没有全部回滚
--force:production环境下强制执行(如果没有带该参数,会询问是否强制执行)


php artisan migrate:reset {--step} {--pretend} {--force} 会执行所有迁移文件的回滚操作(可以指定回滚的step)
--step:回滚几步,没有全部回滚
--force:production环境下强制执行(如果没有带该参数,会询问是否强制执行)
--pretend:加了这个参数,不会真正执行迁移,会输出迁移需要执行的sql,参数没有绑定,会输出【?】



php artisan migrate:rollback {--step} {--pretend} 只回滚最后一条迁移
--step:回滚几步,没有回滚最后一步
--pretend:加了这个参数,不会真正执行迁移,会输出迁移需要执行的sql,参数没有绑定,会输出【?】

后面的英文已经标注了migrate命令的用法,这里实际操作一下:
按顺序:

第一步:创建迁移文件
php artisan make:migration create_table_user --table=user  
//后面的参数分别表示一个迁移文件的标识,和指定需要操作的表
php artisan make:migration create_table_user --create=user  
//后面的参数分别表示一个迁移文件的标识,和需要创建的表

执行后会生成一个迁移文件如图
在这里插入图片描述
每一个文件根据第一个参数会生成一个类,所以这里不能传入重名的参数,而且不能以数字开始,需要符合类的命名规范

第二步:修改迁移文件,写入需要做的事情

这里来点实际操作的,假设我要新增一张表,user,有r这些字段,id,name,account,create_at,update_at,delete_at
代码如下

    public function up()
    {
        Schema::create('user', function (Blueprint $table) {
            $table->increments('id');
            $table->string('name', 50)->default('');
            $table->string('account', 50)->default('');
            $table->tinyInteger('status')->default(0);
            $table->mediumInteger('status1')->default(0);
            $table->integer('create_at')->default(0);
            $table->integer('update_at')->default(0);
            $table->integer('delete_at')->default(0);
            $table->primary('id');
            $table->unique('account','account');
            $table->index(['name', 'delete_at'],'index_name');
        });
    }
第三步:执行迁移(数据库变更)

php artisan migrate //该指令会执行未执行过的迁移文件
这里我是没看出什么问题,但是报错了,而且表建成功了

CREATE TABLE `user` (
	`id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
	`name` VARCHAR(50) NOT NULL DEFAULT '' COLLATE 'utf8_unicode_ci',
	`account` VARCHAR(50) NOT NULL DEFAULT '' COLLATE 'utf8_unicode_ci',
	`status` TINYINT(4) NOT NULL DEFAULT 0,
	`status1` MEDIUMINT(9) NOT NULL DEFAULT 0,
	`create_at` INT(11) NOT NULL DEFAULT 0,
	`update_at` INT(11) NOT NULL DEFAULT 0,
	`delete_at` INT(11) NOT NULL DEFAULT 0,
	PRIMARY KEY (`id`)
)
COLLATE='utf8_unicode_ci'
ENGINE=InnoDB
;

这就很尴尬了,执行回滚也不行,异常了,还没写入数据库,只能先删表了(这在线上肯定不能这么搞),
问题一
把主键的去掉,再执行一下

    public function up()
    {
        Schema::create('user', function (Blueprint $table) {
            $table->increments('id');
            $table->string('name', 50);
            $table->string('account', 50)->default('');
            $table->tinyInteger('status')->default(0);
            $table->mediumInteger('status1')->default(0);
            $table->integer('create_at');
            $table->integer('update_at')->default(0)->unique();
            $table->integer('delete_at')->default(0);
            $table->unique('account','account');
            $table->index(['name', 'delete_at'],'index_name');
        });
    }
CREATE TABLE `user` (
	`id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
	`name` VARCHAR(50) NOT NULL COLLATE 'utf8_unicode_ci',
	`account` VARCHAR(50) NOT NULL DEFAULT '' COLLATE 'utf8_unicode_ci',
	`status` TINYINT(4) NOT NULL DEFAULT 0,
	`status1` MEDIUMINT(9) NOT NULL DEFAULT 0,
	`create_at` INT(11) NOT NULL,
	`update_at` INT(11) NOT NULL DEFAULT 0,
	`delete_at` INT(11) NOT NULL DEFAULT 0,
	PRIMARY KEY (`id`),
	UNIQUE INDEX `account` (`account`),
	UNIQUE INDEX `user_update_at_unique` (`update_at`),
	INDEX `index_name` (`name`, `delete_at`)
)
COLLATE='utf8_unicode_ci'
ENGINE=InnoDB
;

建表算是成功了,但是看了一下源码发现有如下情况:
1,默认所有字段都是not null ,其他的unsiged, default,comment都是没有的,需要手动加
2,字符集默认使用数据库配置的字符集,也可以自己指定,表引擎也是一样
3,int没有指定位数,只有int bigint dediumint tinyint
4,increments自动当作pramiry key,而且后面再加上会报错
5,rollback风险很高,除了rollback,还有reset,refresh同样风险很高,建议线上关闭
6,索引会当作一条单独的语句
看了源码算是有个底了,因此继续实践,那么现在的目标如下:
1,新建三张线上字段比较多的表
2,给三张表,各新增一个字段,并加上索引
3,给三个字段初始化一定的数据
4,给原表的字段修改一些数据,并做备份
5,感觉封装有点问题,nullable那些还要去查,不能
6,索引算法如果没有指定,使用默认的

执行脚本生成迁移文件:

php artisan make:migration create_table_employee --create=employee  
    public function up()
    {
        Schema::connection("mysql");
        Schema::create('employee', function (Blueprint $table) {
            $table->engine = "innodb";
            $table->charset = "utf8";
            $table->collation = "utf8_general_ci";
            $table->increments('id');
            $table->integer('company_id')->default(0)->comment("company_id");
            $table->string('uid', 2058)->default('')->comment("uid");
            $table->string('account_id', 2058)->default('')->comment("account_id");
            $table->string('user_id', 2058)->default('')->comment("user_id");
            $table->string('name', 2058)->default('')->comment("name");
            $table->enum('id_type', [1,2])->default(1)->comment("id_type");
            $table->string('id_code', 2058)->default('')->comment("id_code");
            $table->enum('status', ['IN','OUT'])->default('IN')->comment("status");
            $table->string('field_id_7', 500)->default('')->comment("field_id_7");
            $table->integer('delete_time')->default(0)->comment("delete_time");
            $table->integer('update_time')->default(0)->comment("update_time");
            $table->unique(['uid','company_id','delete_time'],'company_id_uid');
            $table->unique(['company_id','id_code(136)','id_type','delete_time'],'company_id');
            $table->index(['company_id','name(136)','delete_time'],'company_id_name');
            $table->index(['company_id','user_id(136)','delete_time'],'company_id_user_id');
        });
    }

执行一下,有报错,

SQLSTATE[42000]: Syntax error or access violation: 1071 Specified key was too long; 
max key length is 3072 bytes 

又是很尴尬了,索引长度太长了,这里需要指定索引长度的,但是怎么指定???
sql是在字段后面加个field(10),这里加了也没有,又是一个坑,还能不能愉快的玩耍了???,搜了一下好像解决不了,这种特殊的只能直接执行statement了,

解决指定索引长度
//这里有有一种特殊的情况,指定索引长度(加密的uid等字段需要),不支持,可以这样写
$connection = DB::connection("mysql");
$sql = 'ALTER TABLE `table_name` ADD index `index_name2` (`id`, `name`(10), `u_id`) ';
$connection->statement($sql);
解决不支持枚举类型
 $connection = DB::connection("mysql");
$sql = 'desc `table_name` `type`';
$ret = $connection->select($sql);

$typeEnumList = explode(',',str_replace(['(',')','enum'],'',$ret[0]->Type));
$typeEnumList[] = "'new_type'";
$typeEnumStr = implode(',', $typeEnumList);
$sql = "ALTER TABLE `table_name` CHANGE COLUMN `type` `type` ENUM($typeEnumStr) NOT NULL DEFAULT 'type1'";
$connection->statement($sql);
执行sql update
$connection = DB::connection("mysql");
$sql = 'select * from `table_name` where `id` = 10;';
$ret = $connection->statement($sql);
$sql = 'update `table_name` set `name` = \'name\' where `id` = 10';
$connection->statement($sql);

总结

1,lumen 的migration初次使用还是有点不太顺利,主要原因还是不熟悉,后面看了几遍源码之后,migration的规则基本就清楚了,写起来也就更加得心应手了
2,注意线上执行的话,要加异常处理,否则不知道执行到哪一步失败了,然后又重复执行了可能出现不可预期的结果
3,rollback这种功能,会删表的,建议生产环境禁用吧,否则不小心执行了后果很严重
4,有一些类型不支持,像枚举和索引长度,这种可以换成直接执行statement执行sql也能解决
5,多看源码,也花不了多少时间,有些指令的参数,官方文档中并没有写,但是很有用,使用好的话,可以防止踩很多坑

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值