数据库版本管理:springboot + flyway 、shell脚本维护

最近在公司项目中,随着产品的迭代,需要维护数据库表结构、数据升级等问题。最初采用了平台数据库版本维护框架,需要写shell脚本,然后配置rundeck节点实现数据库一键升级。

简单的介绍.sh维护思路:

首先,数据库中,维护一张表history记录数据库版本,类似于flyway的flyway_schema_history表。在history中记录几个关键数据:exe_time、git_commit_id、db_name、sql_type等。

其次,为了使得DDL数据可重复执行,需要在数据库中编写存储过程。调用存储过程执行DDL。

第三,shell脚本逻辑:

在rundeck执行部署计划时,首先去history中查找最近一次执行部署记录;如果没有,则拉取仓库中所有*.sql文件,然后顺序执行文件中的SQL脚本。脚本文件按照文件名排序。

如果有记录,则对比文件改动日期,拉取仓库改动日期晚于最后一次执行部署记录的所有*.sql文件,然后顺序执行文件中的SQL脚本。脚本文件按照名称排序。

核心shell脚本如下:

#执行脚本函数

function sql_sync_frame(){
    execmysqlbin="/usr/bin/mysql -h${host} -P${port} -u${user} -p${password} $2"
    sql_file=$(cat $1)
    while read line
    do
        exec_info="$(date +"%Y-%m-%d %H:%M:%S") 正在执行DDL sql变更:$sql_base_dir/${line}"
        if [ -z ${line} ]; then
          echo "空行跳过"
        fi
        echo ${exec_info}
        ret_info=`$execmysqlbin --default-character-set=utf8 < $sql_base_dir/${line} 2>&1`
        if [ $? == 1 ]; then
            echo -e "\e[31m执行$sql_base_dir/${line}出现异常,退出更新脚本!\e[0m"
            while read line; do
                echo -e "\e[31m${line}\e[0m"
            done< <(echo $ret_info)
            exit 1
        fi
    done< <(echo -e "$sql_file")
}

#获取需要执行的脚本

function diff_git_sql(){
    res=`${mysqlbin} "SELECT comit_id FROM sql_history WHERE sql_type='data' AND db = '${1}';"`
    hist_commit_id=`echo ${res}| awk '{print $2}'`
    echo -e "hist_commit_id:$hist_commit_id"
    if [[ "$hist_commit_id" = "" ]]
    then
        echo -e "没有查询到执行记录"
        first_start="first"
        find . -type f  -name "*.sql"| sort >change_all.txt
        cur_commit_id=`git rev-parse HEAD`
    elif [[ "$hist_commit_id" = "`git rev-parse HEAD`" ]]
    then
        echo -e "\e[1;32mgit没有最新提交的sql文件\e[0m"
        exit 0;
    else
        cur_commit_id=`git rev-parse HEAD`
        echo -e "cur_commit_id:$cur_commit_id"
        git diff --name-only $hist_commit_id $cur_commit_id |grep ".*sql$"| sort > change_all.txt
    fi
    cat change_all.txt | awk -F"\/" '{print $1"/"$2}'|sort|uniq > change_category.txt
    change_category_file=$(cat change_category.txt)
    while read line
    do
        grep "$line" change_all.txt |grep -v ".*mix.sql$" >run_sql.txt
        grep "$line" change_all.txt |grep ".*mix.sql$" >>run_sql.txt

        sql_sync_frame run_sql.txt ${1}
    done< <(echo -e "${change_category_file}")

    if [[ "$first_start" = "first" ]]
    then
        `${mysqlbin} "insert into sql_history (comit_id,sql_type,db) values('${cur_commit_id}','data','${1}');"`
    else
        `${mysqlbin} "update sql_history set comit_id='${cur_commit_id}' where sql_type='data' AND db = '${1}';"`
    fi

}

 

通过这个shell脚本可以完成数据库的维护,但是出现错误后无法回滚,直接跳出。所以我们在写DML语句时,均要求所以语句是幂等的。否则修改错误脚本后,再次执行,会导致脏数据的产生。但是目前而言,很多中初级Java程序员对shell脚本,导致运维的时候比较码放。所以寻求一种Java程序员可以快速掌握和维护的方法。就找到了flyway。当然还可以使用liquibase 。

FLYWAY介绍


可以直接访问我的github项目,该项目通过SpringBoot+Flyway实现数据库版本维护,其中数据库连接使用jdbc。

DEMO连接:https://github.com/wanghowie/flywaydemo.git

DEMO核心配置文件:

spring:
  application:
    name:  dbmanage #服务名
  profiles:
    active: default # 配置文件的版本
  datasource:
    url: jdbc:mysql://127.0.0.1:3306/world?useUnicode=true&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=UTC
    username: root
    password: 1234
    driver-class-name: com.mysql.cj.jdbc.Driver
#  jpa:
#    hibernate:
#    naming:
#      physical-strategy: org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl
#  show-sql: true

  flyway:
    enabled: true
    baseline-on-migrate: true
    validateMigrationNaming: false
    createSchemas: false
    #initSql: CREATE IF NOT EXISTS flyway_history_schema
    url: jdbc:mysql://127.0.0.1:3306/world?useUnicode=true&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=UTC
    schemas: world
    user: root
    password: 1234

使用springboot+flyway所需的依赖:

        <dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-jdbc</artifactId>
		</dependency>
		<dependency>
			<groupId>mysql</groupId>
			<artifactId>mysql-connector-java</artifactId>
		</dependency>

I、官网:https://flywaydb.org/
II、关于SQL脚本

SQL脚本默认放在./src/resources/db/migration,可以通过spring.flyway.locations修改

SQL脚本命名规范如下:
1、Prefix 前缀:V 代表版本迁移,U 代表撤销迁移(暂不支持),R 代表可重复迁移(内容发生变化后,脚本重复执行)
2、Version 版本号:版本号通常 . 和整数组成,前缀为R的脚本文件不能存在版本号--flyway_schema_history中的version属性值
3、Separator 分隔符:固定由两个下划线 __ 组成
4、Description 描述:由下划线分隔的单词组成,用于描述本次迁移的目的--flyway_schema_history中的description 属性值
5、Suffix 后缀:如果是 SQL 文件那么固定由 .sql 组成,如果是基于 Java 类则默认不需要后缀

III、版本管理表

系统会自动创建flyway_schema_history用于记录所有版本演化和状态,其表结构如下(以 MySQL 为例):
Field               Type          Null       Key        Default
version_rank       int(11)            NO         MUL        NULL
installed_rank     int(11)            NO         MUL        NULL
version                varchar(50)        NO         PRI        NULL
description            varchar(200)   NO                NULL
type               varchar(20)        NO                NULL
script             varchar(1000)  NO                NULL
checksum           int(11)         YES                   NULL
installed_by       varchar(100)   NO                NULL
installed_on       timestamp      NO                CURRENT_TIMESTAMP
execution_time     int(11)            NO                NULL
success                tinyint(1)     NO         MUL        NULL

其中:
checksum:Flyway 会给脚本计算一个 checksum 保存在数据库中,用于在之后运行过程中对比 sql 文件是否有变化,如果发生了变化,则会报错,也就防止了误修改脚本导致发生问题。

IV、flyway事务

默认情况下,Flyway总是将整个迁移的执行封装在单个事务中。也可以设置group属性等于true,将未执行的迁移封装到一个事务中。
对于SQL迁移,可以指定脚本配置属性executeInTransaction。

V、Schema creation

如果createSchemas设置为false,可能出现如下情况:

flyway.createSchemas=false
flyway.schemas=my_schema


1、运行迁移
2、my_schema 不是由Flyway创建的
3、由于my_schema是默认Schema,因此Flyway尝试在my_schema创建架构历史记录表
4、my_schema不存在,因此操作失败

因此,当切换createSchemas到时false,建议进行以下设置:
将默认Schema设置为 flyway_history_schema,通过设置defaultSchema或将其放在schemas配置选项中的第一位,设置initSql为创建flyway_history_schema(如果不存在)
将其他架构放在schemas属性中

flyway.createSchemas=false
flyway.initSql=CREATE IF NOT EXISTS flyway_history_schema
flyway.schemas=flyway_history_schema,my_schema

此时,将发生以下情况:
1、运行迁移
2、initSql被执行,所以flyway_history_schema被创建
3、由于flyway_history_schema是默认schema,因此Flyway尝试在flyway_history_schema创建架构历史记录表
4、my_schema 不是由Flyway创建的
5、迁移正常进行
6、迁移可自由控制my_schema的创建

VI、Migration States(迁移状态)

1、迁移已解决或已应用。Flyway的文件系统和类路径扫描程序已检测到已解决的迁移。最初,它们是未决的。一旦对数据库执行了它们,它们就会被应用。
2、迁移成功后,在Flyway的架构历史记录表中将其标记为成功(success)。
3、当迁移失败并且数据库支持DDL事务时,将回滚迁移,并且架构历史记录表中不会记录任何内容。
4、如果迁移失败并且数据库不支持DDL事务,则在模式历史记录表中将迁移标记为失败(failed),表明可能需要手动清理数据库。
5、已被撤消迁移撤消其影响的版本化迁移被标记为“撤消”(undone)。
6、自从上次应用以来,其校验和已更改的可重复迁移被标记为过时(outdated),直到再次执行它们为止。另请注意,更改占位符的值将导致可重复的迁移被视为过时(outdated)的迁移。
7、当Flyway发现所应用的版本迁移的版本高于已知的最高版本时(通常是在较新版本的软件已迁移该架构时发生),该迁移被标记为未来(future)。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值