数据库代码化(Database-as-Code)实战(二)
前言
在数据库代码化(Database-as-Code)实战一文中介绍了如何借助 Flyway 实现 migration based 的数据库迁移。但在实践过程中,发现了如下问题:
- 随着项目的发展,迁移脚本数量会越来越多,而全新部署时由于要执行所有的历史变更,部署时间会越来越长。
- 由于数据库的最终状态是由变更脚本依次执行形成的,这就导致了开发人员无法通过源码直观看到数据库的当前状态。
- 因为很多数据迁移场景涉及到字段的解析以及和第三方系统或工具的交互,使用 Python 脚本实现迁移过程会更加方便。但目前 Flyway 只支持执行 SQL 类型的迁移脚本。
为了解决上述问题,我们基于 migration based 方法,并借鉴了 Flyway 的设计思想,改进了原有的数据库代码化方案。
数据库代码化改进方案
迁移脚本命名规范
迁移脚本命名规范参考了 Flyway 的标准,但也增加了一些限制,下面对其进行说明:
- Prefix - 固定为
V
。 - Version - 由日期和索引组成,格式固定为
yyyy.mm.dd.index
。其中 index 长度固定为 3,范围是 000 ~ 999,用于区分当天新增的不同迁移脚本。 - Separator - 固定为两个下划线
__
。 - Description - 描述信息,文字之间可以用下划线或空格分隔。
- Suffix - 后缀标识,支持
.sql
和.py
。
元数据表结构
和所有 migration based 方案类似,该方案会在目标数据库中创建一个名为schema_version_history
的元数据表用于记录变更信息,具体表结构如下。
mysql> describe schema_version_history; +--------------+--------------+------+-----+-------------------+-------+ | Field | Type | Null | Key | Default | Extra | +--------------+--------------+------+-----+-------------------+-------+ | version | bigint(20) | NO | PRI | NULL | | | description | varchar(200) | YES | | NULL | | | installed_on | timestamp | NO | | CURRENT_TIMESTAMP | | | type | varchar(20) | YES | | NULL | | +--------------+--------------+------+-----+-------------------+-------+
下面对各字段的含义作简要说明。
- version - 已成功应用到数据库上的迁移脚本的版本信息,字段值会从迁移脚本名中提取。例如,
2019.11.11.003
会被转换成整数20191111003
进行存储。 - description - 已成功应用到数据库上的迁移脚本的描述信息,字段值会从迁移脚本名中提取。
- installed_on - 迁移脚本成功应用到数据库上的时间。
- type - 迁移脚本的类型标识(NORMAL、LEGACY、PRE INSTALL、POST INSTALL、UPGRADE EMPTY、OUT OF ORDER)。
迁移脚本组织结构
改进方案的迁移脚本组织结构如下:
|--{db1} |--db.conf |--init_scripts |--a.sql |--b.sql |... `--z.sql |--upgrade_legacy_private_cloud_scripts |--V0666.00.00.000__alter_TB_a_add_column.sql |--V0666.00.00.001__change_to_new_index.py `--V0666.00.00.002__alter_TB_c_add_column.sql |--upgrade_scripts |--V2019.11.11.000__alter_TB_b_add_column.sql |-- |--V2019.11.11.001__TB_c_insert.sql `--V2019.11.13.000__migrate_legacy_alert_rule.py |--{db2} |--db.conf ... |--common |--procedure.sql `--schema_version_history.sql
下面对其进行说明:
- 每个数据库对应一个独立的目录,包含了该数据库的迁移脚本和配置信息。
- 数据库目录下的文件
{db}/db.conf
包含了该数据库的连接、认证等信息。 - 子目录 init_scripts 用于存放数据库的最新 schema。
- 子目录 upgrade_legacy_private_cloud_scripts 用于存放专有云老版本到新版本的迁移脚本。版本号需要小于全新部署时的前置版本号
10000000000
。 - 子目录 upgrade_scripts 统一存放公有云和专有云的后续迁移脚本。版本号由当前期间和索引组成,大于全新部署时的后置版本号
20000000000
。
可以看到,和原方案相比,新方案有如下改变:
- 增加了 SQL 文件
common/schema_version_history.sql
用于初始化元数据表。 - 去掉了用于存放存量 schema 的目录
{db}/base_scripts
。 - 新建目录
{db}/init_scripts
用于存放数据库的最新 schema,全新部署时将直接执行该目录下的 SQL 脚本,免去了执行所有历史变更的过程。 - 支持执行 SQL 和 Python 类型的迁移脚本。
执行流程
基于上述迁移脚本的管理模式,公有云和专有云不同场景的执行流程如下:
全新安装 or 升级
不能单纯根据数据库是否为空判断当前应该执行全新安装步骤还是升级步骤,因为程序有可能在执行全新安装步骤时创建了若干张表后异常退出。这里采用的方案如下:
- 在执行 init_scripts 中的脚本之前,向元数据表
schema_version_history
中插入一条 version 为10000000000
的记录。 - 如果 init_scripts 中的脚本全部执行成功,则将 upgrade_scripts 目录中脚本的最新 version 插入
schema_version_history
中。 - 如果 upgrade_scripts 目录为空,则向
schema_version_history
中插入一条 version 为20000000000
的记录。
这样即使程序中途退出,再次启动后只要发现数据库的版本为10000000000
,就继续执行全新安装的步骤。
脚本的可重入性
每一个迁移脚本的成功执行都对应着schema_version_history
中的一条记录。如果迁移脚本是 SQL 文件,并且是单纯的 DML,则可以将迁移脚本和迁移记录的插入封装在一个事务中执行,从而避免出现状态不一致。但对于包含 DDL 的 SQL 或是 Python 类型的迁移脚本,显然无法通过事务保证迁移脚本和迁移记录的插入同时成功或失败。因此,这里采用了先执行迁移脚本,再进行迁移记录插入的策略。这就对迁移脚本的可重入性提出了要求。让脚本具备可重入性的通用方法可参考幂等性实践。
迁移脚本执行时机
应用升级过程中的数据迁移可能发生在多个阶段,下图展示了某个常见的升级场景。
- 应用开始升级前需要进行一些表结构的变更(数据迁移),支持应用升级后数据以新的格式写入。
- 应用的升级过程是分批次灰度进行的,此时数据有可能以旧的格式写入。
- 应用的全部实例完成升级后,需要对升级过程中产生的旧数据进行订正(数据迁移)。
如果严格按 version 大小判断脚本是否需要执行,则有可能出现数据修正脚本无法执行的情况。为此,我们将迁移脚本分成了 pre_upgrade 和 post_upgrade,对于 post_upgrade 中的脚本,只要在schema_version_history
中不存在对应的执行记录,就允许它执行。
总结
和原方案相比,改进后的方案让全新部署场景下数据库的初始化时间不会随着迁移脚本的增加而延长,同时也可以通过源码直观看到数据库的当前状态,另外也支持了 Python 类型迁移脚本的执行。但这些改进也是有一定代价的,它要求开发人员在进行数据库变更时,既要增加迁移脚本,也要修改数据库初始化脚本。为了防止开发人员的遗漏,建议对数据库代码化部分执行更加严格的代码合入和代码 review 策略。
低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
02月22日云栖号头条:云公益基金会与6支顶级院士团队合作,支持抗疫科研攻关
云栖号:https://yqh.aliyun.com第一手的上云资讯,不同行业精选的上云企业案例库,基于众多成功案例萃取而成的最佳实践,助力您上云决策! 今日最新云头条快讯: 自1月29日宣布捐赠1亿元用于支持新冠肺炎治疗药物和疫苗研发以来,马云公益基金会已经与中国工程院、中国科学院、哥伦比亚大学、钟南山医学基金会等多个顶级科研机构完成签约,立项课题总数超10个;“目前,我们已经开仓的有13家方舱医院,可以提供救治的床位达到了13348张,目前已经使用了9313张,前期存在的患者收治难的问题已经得到了有效的解决。”胡亚波表示。 一起来看最新的资讯: 云公益基金会与6支顶级院士团队合作,1亿元捐赠支持抗疫科研攻关 自1月29日宣布捐赠1亿元用于支持新冠肺炎治疗药物和疫苗研发以来,马云公益基金会已经与中国工程院、中国科学院、哥伦比亚大学、钟南山医学基金会等多个顶级科研机构完成签约,立项课题总数超10个。 达摩院:医疗AI已在26家医院上岗,已诊断3万个疑似肺炎病例 据达摩院方面介绍,达摩院医疗AI已在湖北、上海、广东、江苏等16个省市的26家医院上岗,截至目前,达摩院AI已对3万个临床疑似...
- 下一篇
UKUI 3.0 揭秘第二弹——任务栏惊艳来袭!
《Must Read:Okay Wow:The New UKUI Desktop Looks Phenomenal》 UKUI3.0的开始菜单预览视频一经发布,便受到了国内外诸多Linux爱好者的关注。国际著名开源社区网站《omg!ubuntu!》上将一篇介绍新版UKUI的文章推荐为必读,文章标题甚至使用“Phenomenal”一词形容全新的UKUI桌面环境,对UKUI3.0桌面环境给予了极高的评价。 全新UKUI 3.0任务栏,又将带来哪些惊喜呢? ( 点击上图或链接https://www.bilibili.com/video/av90660949进入视频预览) 优麒麟开源桌面操作系统创建于2013年,已累计发行14个版本,是Ubuntu的官方衍生版本之一。UKUI是由麒麟团队开发的基于Linux发行版的轻量级桌面环境,默认搭载在优麒麟开源操作系统和银河麒麟/中标麒麟商业发行版中。
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
推荐阅读
最新文章
- CentOS8安装Docker,最新的服务器搭配容器使用
- SpringBoot2整合MyBatis,连接MySql数据库做增删改查操作
- CentOS8,CentOS7,CentOS6编译安装Redis5.0.7
- 设置Eclipse缩进为4个空格,增强代码规范
- Docker使用Oracle官方镜像安装(12C,18C,19C)
- CentOS8编译安装MySQL8.0.19
- SpringBoot2整合Redis,开启缓存,提高访问速度
- SpringBoot2配置默认Tomcat设置,开启更多高级功能
- Hadoop3单机部署,实现最简伪集群
- CentOS7,CentOS8安装Elasticsearch6.8.6