如何对DevOps数据库进行源代码控制

提纲:

  • 包括索引在内的数据库模式需要进行源代码控制
  • 诸如查询表这类用于控制业务逻辑的数据需要进行源代码控制
  • 开发人员需要一种能够便捷地创建本地数据库的方法
  • 共享数据库的更新只能通过构建服务器完成

健壮的DevOps环境需要对系统的每个组件进行持续集成。但是,数据库常常被排除在这之外,这会导致从脆弱的产品发布和低效的开发实践到让新入职的程序员工作更困难等一系列问题。

在本文中,我们将讨论在成功的持续集成环境中,关系型数据库和NoSQL数据库的独特的一面。

模式的源代码控制

首先需要解决的就是源代码控制和模式问题。让开发人员以一种特别的方式进行数据库变更是不合适的。这相当于在生产服务器上通过直接编辑JavaScript文件对其进行更改。

在为数据库规划源代码控制时,需要确保囊括了所有内容。这包括但不限于:

  • 表或者集合
  • 约束
  • 索引
  • 视图
  • 存储过程、函数以及触发器
  • 数据库配置

您可能会想,“我使用的是无模式数据库,所以我不需要源代码控制”。即便如此,您仍需要考虑索引和数据库的整体配置。如果在QA和生产数据库中索引计划不同,那么执行性能测试将毫无意义。

数据库的源代码控制有两种基本类型,我们将其称为“全模式”和“变更脚本”。

全模式源码控制

“全模式”源码控制指的是源代码控制与你所希望的数据库的外观看起来十分相似。在使用此模式时,可以看到所有的表和视图都按照预期的样子排列,这让你无需部署数据库就能够更容易理解它。

SQL Server的SQL Server Data Tools(SSDT)就是全模式源码控制的一个例子。这个工具可以通过CREATE脚本的形式表示所有数据库对象。当想要用SQL创建一个新的对象,只需将最终脚本直接粘贴到处于源代码控制下的数据库项目中即可,这就很方便了。

全模式源码控制的另一个例子是实体框架迁移(Entity Framework Migrations)。在这个案例中,数据库主要通过C#/VB类的方式而非SQL脚本的方式表现。但同样,可以通过浏览源代码获得对数据库的整体认识。

在使用全模式源码控制时,通常不需要直接编写迁移脚本。部署工具通过将数据库的当前状态与处于源代码控制中的理想版本进行比较来确定需要进行哪些更改。这可以让你快速完成数据库变更并看到结果。在使用这种类型的工具时,我很少直接更改数据库,而是使用工具完成大部分工作。

有些情况下,只有工具是不够的,即使包含部署前和部署后脚本也是如此。在这种情况下,生成的迁移脚本必须由数据库开发人员或DBA手工修改,这可能会破坏持续部署计划。这种情况通常发生在对表结构进行重大变更时,因为在这些情况下生成的迁移脚本效率可能较低。

全模式源码控制的另一个优点是它支持代码分析。例如,如果列的名称被更改,但在视图中未做出相应更新,SSDT将返回一个编译错误。就像应用程序设计语言中的静态类型一样,可以捕获大量错误,并且能够对部署有明显错误的脚本提前防范。

变更脚本源码控制方式

另一种源代码控制方式就是变更脚本源码控制。这种方式不存储数据库对象本身,而是存储创建数据库对象所需的步骤列表。我曾经成功使用的是Liquibase数据库,总的来说这类工具的工作机制都差不多。

变更脚本工具的主要优点是可以完全控制最终迁移脚本的样貌。这使得复杂变更的执行更加容易,例如表的拆分或合并。

不幸的是,这种类型的源代码控制也存在一些缺陷。首先是需要编写变更脚本。虽然它的控制力度更强,但同样也更费时。相比于手写ALTER TABLE脚本,为C#类添加一个属性要容易得多。

当使用SchemaBinding之类的功能时,会让问题更加严重。对于不熟悉这个术语的人,可以将其理解为,通过锁定表的模式启用SQL Server中的一些高级特性。如果要变更表或视图的设计,必须首先将SchemaBinding从任何与该表或视图有关的视图中移除。如果这些视图被其他视图模式绑定(schema-bound),那么这些视图同样也需要临时解除绑定。虽然对于全模式源代码控制工具来说,生成所有的样板很容易,但是想要用手工的方式正确地生成它们还是一项相当困难的工作。

变更脚本工具的另一个缺点是它们往往难以理解。例如,如果你希望在不部署表的情况下了解某个表中列的信息,则需要查阅所有涉及该表的变更脚本。这就很容易错过某些信息。

该如何选择源代码控制模型?

从头开始一个新的数据库项目时,我会选择全模式源代码控制。这种模式会让开发人员工作更加高效并且只要有一名人员完成设置工作,之后只需要很少的知识就可以正常使用。

对于现存的数据库,特别是那些已经存续多年的生产环境数据库,通常选择变更脚本源代码控制模式更加合适。全模式工具会对数据库设计做出某些特定的假设,而更改脚本工具则是通用的。此外,全模式工具构建难度更大,对于某些特殊的数据库来说,可能根本不可用。

数据管理

根据表中所含数据的性质,表可以被广泛地分类为“管理表”、“用户表”或“混合表”。根据表所属的类别不同,处理这些表的方式也是不同的。

管理表

将数据库置于源代码控制之下的一个常见错误是遗忘数据。总有一些“查询表”保存着用户不打算修改的数据。例如,其中可能包含表驱动的业务规则逻辑、状态机的各种状态码,或者仅仅是与应用程序代码中的枚举类相匹配的键-值对列表。

这类表中的数据应该被视为源代码一样对待。对这类数据的变更需要经过与其他代码变更相同的评审和QA过程。特别重要的是,为确保这些变更不会被遗漏,这些变更的部署应该与其他应用程序和数据库部署一并自动完成。

在SQL Server数据工具中,我使用部署后脚本处理此问题。在这个脚本中,我将期望数据填充到一张临时表中,然后使用MERGE语句更新实际表。

如果源代码控制工具无法很好地处理这个问题,还可以通过构建独立工具来执行数据更新。重要的不是如何做,而是这个过程是否易用并且可靠。

用户表

用户表指的是用户可以添加或修改数据的表。因此,这包括可以直接修改的表(例如名称和地址)和通过操作间接修改的表(例如货单收据、日志)。

真实的用户数据基本不会被直接加入源代码控制中,不过,为开发和测试提供逼真的样本数据也是一种最佳实践。这些数据可以直接存储在数据库项目中,处于源代码控制之下的其他地方,如果特别大,也可以保存在独立的共享文件中。

混合表

混合表指的是即存储管理数据也存储用户数据的表。有两种方法可以对其进行分区。

列分区是指用户可以修改某些列,但不能修改其他列。在这种场景下,可以将该表视为有额外限制的普通管理表,用户控制的列永远不会被更新。

行分区指的是某些记录用户无法修改的情况。我曾经遇到的常见的场景是需要在用户表中对某些值进行硬编码。在较大型的系统中,对于每个可以独立于任何真实用户进行更改的微服务,可能都有一个独立的用户ID。例如,可能是一个被称为“银行数据导入器”的用户。

在我看来,管理行分区混合表的最佳方法是通过保留键的方式。当定义identity/auto-number列时,将初始值设置为1,000,通过源代码控制对编号从1到999的用户ID进行管理。这需要数据库允许手动设置identity列中的值。在SQL Server中,是通过SET_IDENTITY_INSERT命令完成的。

处理此场景的另一个选择是使用名为“SystemControlled”的列或者能够达到类似效果的方法。当设置为1/true时,表示应用程序不可直接修改。如果设置为0/false,则部署脚本会将其忽略。

个人开发数据库

将模式和数据置于源代码控制之下,就可以进行下一步并着手设置个人开发数据库。正如每个开发人员都应该能够运行自己的web服务器实例一样,有可能对数据库设计做出修改的每个开发人员都需要能够运行自己的数据库副本。

这一规则经常会被打破,这对开发团队是相当不利的。在共享环境上做变更的开发人员一定会相互干扰。他们甚至可能会陷入“部署之争”,每个开发人员都试图将更改部署到数据库。当使用全模式工具执行此操作时,开发人员将交替地恢复其他开发人员的变更,甚至都不会意识到这一点。在使用变更脚本工具的情况下,数据库则可能处于不确定状态,迁移脚本可能无法正常使用,需要从备份中重新恢复。

另一个问题是模式漂移。这时,开发数据库与处于源代码控制下的内容不再匹配。随着时间的推移,开发数据库会逐渐积累越来越多的非生产表、测试脚本、临时视图和其他垃圾信息需要清除。当每个开发人员都有自己的数据库时,这样做要容易得多,因为他们可以随时重置数据库。
最后也是最重要的问题是,服务开发人员和UI开发人员需要稳定的平台来编写代码。如果共享数据库不断变化,他们就无法有效地工作。在我曾经工作过的一家公司,很少看到开发人员大喊“服务又宕机了!”,然后玩一个小时视频游戏,等待共享数据库重新组装起来。

共享的开发和集成数据库

对于共享的开发人员数据库或集成数据库来说,首要原则就是不能对数据库直接进行修改。更新共享数据库的唯一方法就是通过构建服务器以及持续集成/部署流程完成。这不仅可以防止模式漂移,还可以通过有计划的更新减少中断的次数。

根据经验,我会在相关分支进行代码检入时同步更新共享开发人员数据库。这可能会造成中断,但通常是处于可控范围的。在进行集成之前,您确实需要先验证部署脚本的正确性。

对于集成数据库而言,我更倾向于每日安排一次部署,与服务的部署次数相同。这样能够为UI开发人员提供一个相对稳定的工作平台。对于UI开发人员来说,没有什么比不知道突然开始失败的代码是他们的错误还是服务/数据库中的问题更令人沮丧的了。

数据库安全和源代码控制

在数据库管理中,安全性是一个经常被忽略的方面。具体来说,就是哪些用户和角色可以访问哪些表、视图和存储过程。在最糟糕的情况下,应用程序可以获得对数据库的完全访问权限。它可以对每个表的每一列进行读写操作,甚至是那些与其没有业务关联的列。数据泄漏经常是由于实用小程序的访问权限远远超过其所需的访问权限而造成的。

锁定数据库的主要反对意见是,“我们无法预知什么会崩溃”。因为以前从未被锁定过,所以开发人员根本不知道应用程序实际需要的权限是什么。

解决这一问题的办法是从第一天起就将权限控制放入源代码控制中。这样,当开发人员测试应用程序时,如果权限不正确,一开始就会失败。这反过来又意味着,当到达QA阶段时,所有权限问题都已解决,不存在权限缺失的猜测或风险。

容器化

根据项目的性质不同,数据库的容器化是一个可选步骤。我将通过两个案例说明个中原因。

对于第一个案例,这个项目有一个非常简单的分支结构:有一个“dev”分支,它会导入QA分支,QA分支又会导入阶段化分支和最终的生产分支。这可以通过四个共享数据库实现,管道中的每个阶段,各使用一个数据库。

在第二个案例中,我们有一组重要的特性分支。每个重要的特性分支被进一步细分为开发和QA分支。每个特性必须通过QA检验,才能够成为合并到主分支的候选特性,因此每个重要特性都需要有各自的测试环境。

在第一个案例中,即使web服务确实需要容器,容器化也可能是浪费时间。对于第二个用例,容器化某种程度上则是至关重要的。如果没有真正的容器(例如Docker),那么在创建新的重要特性分支时,至少可以根据需要生成新环境的部署脚本(例如AWS或Azure)。

关于作者

Jonathan Allen在90年代后期开始为一家医疗诊所开发MIS项目,逐步将该项目从Access和Excel升级成企业级的解决方案。在花了五年时间为金融业编写自动化交易系统之后,他成为了多个项目的顾问,其中包括自动化仓库的UI、癌症研究软件的中间层,以及一家大型房地产保险公司的大数据需求。在空闲时间,他喜欢研究和记录源于16世纪的武术。

查看英文原文: How to Source Control Your Databases for DevOps

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/280445.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

自定义异常最佳实践_播放,自定义和组织媒体的最佳文章

自定义异常最佳实践Computers today are used for much more than generating documents, writing and receiving email, and surfing the web. We also use them to listen to music, watch movies and TV shows, and to transfer media to and from mobile devices. 如今&…

CSS中的路径裁剪样式clip-path

前面的话 CSS借鉴了SVG裁剪的概念,设置了clip-path样式,本文将详细介绍路径裁剪clip-path 概述 clip-path属性可以防止部分元素通过定义的剪切区域来显示,仅通过显示的特殊区域。剪切区域是被URL定义的路径代替行内或者外部svg,或…

socket编程学习笔记

socket编程: 1、网络基础知识 两台计算机通过网络进行通信,首先两台计算机要有唯一的标识,即唯一的IP地址。其次他们要有共同的语言用来交流即协议。再者,每套主机要有相应的端口号。  TCP/IP协议:   --TCP/IP协议是…

rest_framework04:ModelSerializer/Serializer高级用法

ModelSerializer 1.减小序列化类代码 2.不需要重写update,create ser.py class BookModelSerializer(serializers.ModelSerializer):class Meta:modelBookfields__all__ #序列化全部字段# fields(name,price) # 序列化指定字段# exclude(name,) # 与fields 不能…

配置本地及网络yum源(详细步骤)

我们以centos6为范例演示 1、[rootCentos6 ~]# cd /etc/yum.repos.d/ [rootCentos6 yum.repos.d]# ls CentOS-Base.repo CentOS-fasttrack.repo CentOS-Vault.repoCentOS-Debuginfo.repo CentOS-Media.repo先罗列出相关文件 2、[rootCentos6 yum.repos.d]# vim CentOS-Base.rep…

macos mojave_如何修复macOS Mojave上的模糊字体(使用亚像素抗锯齿)

macos mojaveApple’s macOS Mojave disables subpixel antialiasing, also known as font smoothing, by default. On a MacBook Air or a desktop Mac hooked up to a non-Retina display, upgrading will make your fonts look worse. 苹果的macOS Mojave默认情况下禁用子像…

为什么我要写博客

原因在这啦 一、我觉得分享是一种精神,分享是我的乐趣所在,不是说我觉得我讲得一定是对的,我讲得可能很多是不对的,但是我希望我讲的东西是我人生的体验和思考,是给很多人反思,也许给你一秒钟、半秒钟&…

一个变量命名神器:支持中文转变量名

变量命名的规范,对于我们编程,大家都知道是非常重要的,上次给大家推荐过一个命名辅助工具《程序员还在为变量取名苦恼,那是因为你不知道,这个变量命名神器》,但大家一致反馈存在2个问题:1、网速…

rest_framework05:GenericAPIView用法/扩展类5个/子类9个/ViewSetMixin 自定义方法名字

GenericAPIView 1.视图层类使用GenericAPIView继承,能简化类里的方法code。 2.简化后的方法code格式基本通用,简单修改即可应用到其他类。 一、class开始加入 queryset Book.objectsserializer_class BookModelSerializer 二、方法里获取对象 a.查…

1.操作系统概述

2019独角兽企业重金招聘Python工程师标准>>> 操作系统的发展过程 无操作系统的计算机系统单道批处理系统(50年代,系统资源利用率低)多道批处理系统(60年代)分时系统(70年代)实时系统…

测听hl和nhl的区别_播放NHL曲棍球的最便宜方法(无电缆)

测听hl和nhl的区别If you’re like me, you watch hockey, and…basically no other sports. You also, like me, would like to skip the cable subscription. So what’s the cheapest way to watch NHL hockey online so you can cut the cord? 如果您像我一样,…

制作一个让客户满意的软件

我看了《构建之法》的第八章“需求分析”我对如何制作一个让客户满意的软件有了一点儿头绪,的但是还是有一些迷惑。我通过看书总结和百度查找有了一点儿总结:我们在制作软件的过程中应该及时与用户沟通交流,交换意见,并及时实现用…

rest_framework06:自动生成路由\action使用\认证

自动生成路由 # 1.导入routers模块 from rest_framework import routers# 2.实例化类 routerrouters.SimpleRouter()# 3.注册 # (前缀,继承自ModelViewSet视图类,别名) router.register(books7,views.BooksView) # 不要加斜杠# 4.加入 urlpatternsrouter.urls action使用 装…

char data[0]在struct末尾的用法

在实际的编程中,我们经常需要使用变长数组,但是C语言并不支持变长的数组。此时,我们可以使用结构体的方法实现C语言变长数组。 struct MyData { int nLen; char data[0];}; 在结构中,data是一个数组名;但该数组没有元素…

使用Java实现K-Means聚类算法

2019独角兽企业重金招聘Python工程师标准>>> 关于K-Means介绍很多,还不清楚可以查一些相关资料。 个人对其实现步骤简单总结为4步: 1.选出k值,随机出k个起始质心点。 2.分别计算每个点和k个起始质点之间的距离,就近归类。 3.最终中心点集可以划分为…

在PowerShell中显示高级进度条

如果你需要编写一些PowerShell脚本,尤其在处理一些相对复杂的任务时,你可能希望添加进度条的功能,以便随时可以了解进展情况。Write-Progress 这个命令可以帮助你完成简单的需求,请参考官方文档即可,但下图一个示例&am…

当检测到运动时如何自动打开门灯

If it’s dark out and someone comes to your door, you probably can’t see them unless your porch light is on. Furthermore, if a potential burglar approaches your front door, a motion light can help scare them away. 如果天黑了,有人进了您的门&…

分布式系统的那些事儿(六) - SOA架构体系

有十来天没发文了,实在抱歉!最近忙着录视频,同时也做了个开源的后台管理系统LeeCX,目前比较简单,但是后续会把各类技术完善。具体可以点击“原文链接”。 那么今天继续说分布式系统的那些事。 我们现在动不动就讲分布式…

rest_framework07:权限/频率/过滤组件/排序/异常处理封装Response对象

权限 写一个类,继承BasePermission,如果通过返回True,否则False 这里需要配合认证使用,否则没有user_type属性。 from rest_framework.permissions import BasePermissionclass UserPermission(BasePermission):def has_permis…

在阿里,我们如何管理测试环境

为什么80%的码农都做不了架构师?>>> 作者:林帆(花名金戟),阿里巴巴研发效能部技术专家 相关阅读:在阿里,我们如何管理代码分支 前言 阿里的许多实践看似简单,背后却蕴涵…