数据库设计中的敏捷方法

数据库设计中的敏捷方法

引言

过去几年中,我们将敏捷方法应用于数据库设计,总结出一些技巧,使得当应用程序发展时,数据库也能够进化,这是敏捷方法的一个重要属性。我们的方法是通过持续集成以及自动重构,通过数据库管理人员(DBA)和应用开发人员的紧密合作来设计数据库。这些技巧在应用开发的各个时期都有效。

1 敏捷方法学

近年来,出现了一种新的软件开发方法学——敏捷方法学。这给数据库设计提出了一些新的、巨大的需求。这些需求的一个中心就是进化设计。在一个敏捷项目中,需要假定我们并不能事先确定系统的需求,因此在项目的初期有一个详细设计阶段的想法是不现实的。系统的设计必须随着软件的变化而进化。敏捷方法,尤其是极限编程(XP),通过一些实践使这种进化设计成为可能。在数据库设计采用敏捷方法,反复迭代。

许多人会怀疑敏捷方法能否用于有大型数据库组件的系统,但我们的确使用了许多敏捷和XP技巧,用于解决基于大型数据库的项目中的进化与迭代问题。

本文将介绍一些在数据库设计采用敏捷方法的实践。当然,这并不是说我们已经完全解决了数据库进化的问题,但是我们想提供一些行之有效的方法。

2 积极应对变化

敏捷编程的一个显著特点就是它面对变化的态度。对软件过程的一般解释是尽早理解需求,停止需求的变动,将这些需求作为设计的基础,停止设计的变动,然后开始构筑体系,这就是瀑布方法——基于计划的生命周期。

这种方法通过大量的前期工作来减少变化,一旦前期工作完成,需求变化会引起很大的问题。当需求变化时,这样的方法就会有很大的问题,因此需求变动是这种过程的一个很大的问题。

而敏捷编程却以另外一种方式来面对变化、拥抱变化,甚至允许在项目开发的后期发生变化。尽管变化会被控制,但是这种态度会允许尽可能多的变化。变化部分来自于项目需求的不稳定,部分来自于要支持变化的商业环境来面对竞争压力。

为了做到这样,必须采取不同的设计态度。设计不仅仅是一个阶段——在开始建筑之前就大部分完成的一个阶段,设计是一个持续的过程,与编码、测试甚至发布相关,这是计划设计与进化设计的不同之处。敏捷方法的一个重要贡献是提出了在可控制方式下的进化设计,提供了控制进化设计和使其可行的技巧。

敏捷方法的一个重要特点就是迭代式开发,即整个项目生命周期中运行多个完整的软件生命周期循环。敏捷过程在每次迭代中都会度过一个完整的生命周期,迭代可以完成最终产品的需求子集中编码、测试以及集成代码。敏捷方法迭代时间较短,通常是一周到两个月之间,而且我们更倾向于更短的迭代周期。

当使用敏捷方法时,最大的问题就是数据库如何进行进化设计。许多人认为数据库设计是前期计划的工作,而在后期改变数据库设计计划会引起应用软件的崩溃;在配置以后改变数据库设计计划会导致数据迁移问题。

在过去三年我们参加了一个大型的项目,其中用到了切实可行的进化设计的方法。该项目包括100人的项目组,200多张表格,数据库在一年半的最初开发中一直在进化,甚至在为多用户分发的过程中也在进化。一开始我们一个月迭代一次,过了几个月之后变为两周迭代一次。

随着我们将这些经验推广到项目中越来越多的部分,从越来越多的案例中获得经验。同时,我们也从其他敏捷项目中吸收了一些经验。

2.1 限制条件

在讲述实践方法之前,必须指出我们并没有解决所有的数据库进化设计问题,特别是:

(1) 我们是为单独的应用设计一个应用数据库,而不是试图集成多个数据库;

(2) 我们没有做到24*7的数据库更新。

虽然很多人认为我们无法解决这个问题,但其实这些问题是可以解决的。当然这需要进一步的工作,光说是不能解决问题的。

3 实践

我们有关于数据库进化设计的方法依赖于一些重要的实践。

3.1 数据库管理人员与开发人员紧密合作

敏捷方法的一个重要原则就是拥有不同技能和背景的人能够紧密合作。正式的会议和文档不能达到充分交流的效果,因此他们需要一直一起工作、亲密合作。所有的项目组成员都需要紧密合作:系统分析人员、项目经理、行业专家、开发人员以及数据库管理人员(DBA)。

开发人员的每项工作可能都需要DBA的帮助,开发人员和DBA需要考虑是否需要对数据库计划做很大的改变。开发人员向DBA咨询如何应对变化:开发人员知道需要什么新的功能,而DBA对应用中的数据有全局的观念。

为了达到亲密合作的效果,DBA必须使自己易于接近。DBA需要留出几分钟的时间,让开发人员来提问。必须确保DBA和开发人员坐在一起,这样他们就很容易沟通。同时必须确保应用设计会议是公开的,这样DBA可以随时加入进来。在很多情况下我们发现人们在DBA和应用开发人员之间建立屏障,这些屏障必须去除,这样进化数据库设计才有可能。

3.2 每个项目组成员都有自己的数据库实例

进化设计认为人们通过尝试来进行学习,在编程期间开发人员在如何实施某个特征,应用某个首选的方案之前做一些试验,数据库设计也是如此。因此,每个开发人员都有自己用来试验的实例,而不必影响其它人,这一点很重要,这样每个人都可以根据自己的需要进行试验。

许多DBA专家认为多个数据库是一种麻烦,不易于实际应用,但我们发现操作一百个左右的数据库是很容易的。当然其中很重要的是拥有便利的工具,使你像操作文件一样操作数据库。

3.3 开发人员数据库经常集成到共享主数据库

尽管开发人员可以在他们自己的空间频繁试验,但是将不同的工作定期汇合也是很重要的。应用开发需要一个共享主数据库,所有的工作都汇集于此。当开发人员开始工作时他们从主数据库获取拷贝到自己的工作空间,进行操作和修改,然后将变化反馈进入主数据库。我们的规定是每个开发人员要每天提交汇合一次。

假设开发人员上午10点开始一项开发任务,这项任务的一部分是改变数据库计划。如果这种改变很简单,如增加一个字段,他就可以自己决定。通过数据字典的帮助,开发人员还必须确保他想增加的字段数据库中没有,但是如果他与DBA讨论这种可能的变化,那么工作就要简单的多。

当他准备开始时,先从主数据库中获取一份拷贝,这样就可以自由地改变数据库计划和代码。因为他使用的是自己的数据库实例,所以不会影响别人。在某个时候,如下午3点,他很清楚需要什么样的数据库变化,甚至此时他还没有完全做完他的编码工作。这时他找到DBA,告诉他想要的变化,这时DBA可以提出开发人员没有考虑到的问题。当然大多数时候都很好,DBA同意这种变化(通过一个或多个数据库重构)。DBA使变化马上发生(除非他们是破坏性的变化),这样开发人员可以继续他的工作,在任何时候提交代码,因为DBA已经将这些变化发送给主数据库。

可以将这个原则看作类似于持续集成,持续集成常用于源码管理。实际上这就是将数据库看作是另一种源代码,因为配置管理系统象控制源代码一样控制主数据库。只要我们构建成功,数据库和源代码一起被送入配置管理系统,这样我们就有两者完整和同步的版本历史。

对于源代码来说,集成中的问题被源代码控制系统处理。对于数据库来说,要做的工作稍微多一些,所有数据库的变化都需要妥善处理,如自动化数据库重构。此外DBA需要审视任何数据库变化,保证其符合整个数据库的计划。为了使这项工作做的比较平稳,在集成的过程中不应该出现大的变化——因此需要DBA与开发人员紧密合作。

我们强调经常性的小集成,因为它比非经常性的大集成容易得多。集成的复杂度会随着集成的规模呈几何级度增加,因此做许多小的变化在实践中更易于实现,当然这看上去与直觉相抵触。

3.4 数据库包含计划和测试数据

当提到数据库的时候,我们并不仅仅指数据库计划,而且还包括相当规模的数据。这些数据包括应用所需的标准数据,如全国所有的省份名,以及一些样本客户的样本数据。

数据的作用:

(1) 易于测试

使用大量的自动化测试可以帮助稳定应用的发展,这样的测试在敏捷方法里是常用的方法。为了使这些测试有效进行,很理智的方法是在一个有样本测试数据的基础上工作,这样所有的测试可以在程序正式进行之前完成。

(2) 测试数据库的迁移

除了测试代码之外,样本测试数据允许我们测试数据库的迁移,当改变了数据库的计划后,我们还必须保证所有的计划变更也能够处理样本数据。

在大多数项目中这些样本数据是虚构的,然而在某些项目中人们使用实际数据作为例子,在这些情况下,数据从先前由自动化数据迁移代码的系统中提取出来。很明显不能马上迁移所有的数据,因为在早期迭代中数据库只有小部分建立起来。但是我们希望当应用和数据库发展时,改变迁移代码。这样不仅能够尽早解决迁移问题,也使行业专家易于处理这个正在开发的系统。因为有他们熟悉的数据,所以他们会指出可能给数据库和应用设计带来问题的地方,因此我们建议在项目的早期迭代中引入实际数据。
3.5 所有的变化应该数据库重构

重构技术就是应用所有可控技术来改变现有的代码基础,与此类似我们定义了数据库重构也给数据库的改变提供了类似的控制。

数据库重构的不同之处在于它必须将三种不同的变化同时完成:

(1) 改变数据库计划

(2) 进行数据迁移

(3) 改变数据库存取代码

于是当描述数据库重构时,我们必须描述变化的三个方面,并确保在应用另一个重构之前完成了这三种变化。

我们必须文档化不同的数据库重构,因此我们还不能详细描述他们。然而这里有几点需要指出:像代码重构一样,数据库重构非常微小;概念链一系列微小的变化,数据库和代码很相似;变化的三个属性使保持小的变化更加重要。

许多数据库重构,如增加一个字段,可以不必更新所有存取系统的代码来完成。但是如果在使用新计划之前并不了解它,该字段将会无用,因为新计划不知道其变化之处。许多变化,没有考虑整个系统计划,我们称之为破坏性变化,如将一个已经存在的空值列设置为非空。破坏性变化需要多加留心,留心的程度依赖于包含破坏性的程度。一个小破坏性的例子是将一个已经存在的空值列设置为非空,在这种情况下你可以蒙着头做。

而重构将考虑数据库中空值数据,开发人员将更新数据库映射代码,因此更新不会破坏其它人的代码;如果偶然会破坏,开发人员将在建立和使用测试时发现问题。

将一个经常使用的表分成两个是一种更复杂的破坏。在这种情况中提前让所有人知道变化到来很重要,这样他们可以有所准备。此外应该在一个更安全的时刻来实施变化。这里面很重要的一点是选择适用于你做出的变化的过程。

3.6 自动重构

在代码世界中许多语言能够实现自动重构。在计划变化和数据迁移过程中,这种自动化对于数据库也非常重要。因此每个数据库重构都可以通过编写SQL DDL(对于计划变化)和DML(对于数据迁移)来完成。这些变化不是通过手工实现,而是通过一些SQL语句来自动实现变化。

一旦完成代码,我们保存这些代码文件来产生数据库变化的完整的变化记录,作为数据库重构的结果。我们于是可以更新任何实例到最新的主数据库,通过运行在我们拷贝主数据库来产生更早的数据库实例的变化记录。

自动化变化的序列化是对于持续集成和迁移产品数据库的一个基本功能。

为了最后产品数据库我们并不在常规迭代周期中实施变化,我们在每一个发布之间建立完整的数据库重构变化日志。毫无疑问,这是一个巨大的变化,我们必须离线实施该变化,在实际应用之前先测试迁移计划绝对是明智之举。迄今为止,这项技术相当管用,通过将大变化分解为小的简单的变化,我们可以对产品数据进行大的变化,同时又不会给我们太多的麻烦。套用兵法中的一句话,就是“化整为零”。

除了自动化向前的变化,我们也要考虑重构时向后的变化,如果能够做到这样就可以回到以前的数据库状态。在我们的项目中并没有这样做,因为没有这个需求,但这同样是很重要的基本原则。

3.7 自动地更新所有开发人员的数据库

人们变化和更新主数据库,但是如何发现主数据库已经发生变化?在传统的持续集成有源代码的环境中,开发人员在提交变化之前先更新主数据库。这样他们就可以在提交变化给共享主数据库之前,解决他们自己的机器上的问题。

每次主数据库发生改变时,我们都要更新开发人员的数据库。当主数据库发生变化时,我们自动化更新所有项目成员的数据库。相同的重构代码更新主数据库上的同时,自动更新成员数据库。也许有人认为在开发人员不知情的情况下,更新开发人员数据库会产生很多问题,但在实践中我们没发现什么问题。当然,这只在人们联网时管用。所以当开发人员离线时,必须尽快与主数据库重新保持同步。

3.8 清晰地分离所有的数据库获取代码

为了理解数据库重构的结果,了解应用程序如何使用数据库也非常重要。如果SQL语句分布在代码基础周围,则很难这样去做。因此一个清晰的数据库获取层很重要,它用来显示数据库如何被使用,在哪里被使用。

清晰的数据库层有很多好处。它减少了开发人员操纵数据库时需要使用SQL知识的地方,这样使对SQL语句不太熟悉的开发人员更易开发。对于DBA来说,给他提供了清晰的代码,可以清楚地了解数据库将如何使用。这也帮助准备索引、数据库优化,优化SQL语句,使DBA更好地理解数据库如何被使用。

4 变化法则

如同任何实践一样,这些原则必须根据你特殊的环境变化。没有一成不变的项目,我们必须要应对变化。

4.1 保持多个数据库在一个系统中

简单项目也许只需要一个主数据库。但是复杂项目需要有多个数据库,即数据库系。如果在投入生产之前数据库必须分支,那么我们可以创建新的数据库系。数据库系类似于代码的分支,需要不同测试数据集来进行测试。

当开发人员从主数据库中获取了一份拷贝,必须注册他们在修改哪个数据库系。当DBA更新主数据库某个数据库系时,同时更新了所有注册这个数据库系的开发人员的数据库。

4.2 不需要专职的DBA

所有这些听上去好像需要大量的工作,但它并不需要大量的人力资源。在最大的项目中,我们有30个开发人员,项目组规模100人(包括质量评价、分析人员和管理人员),我们大概有100多个不同系列的产品分布在各工作站上。但所有这些工作只需要一个专职DBA,只有两个编程人员业余帮忙。

在小项目中甚至不需要专职DBA。当我们将这些技巧用于更小的项目--12人左右的小项目时,发现该项目不需要一个专职的DBA,与此相反,我们依靠两个对数据库感兴趣的开发人员业余处理DBA任务。

这是自动化的功劳,如果对每项任务进行自动化处理,就可以用更少的人来完成更多的工作。

5 辅助工具

数据库进化需要大量的重复性工作,我们可以开发一些简单工具来帮助我们解决大量的重复性工作。

自动化的最有价值的地方就是有一个通用数据库任务简单代码集。自动化的任务包括:

(1) 用户资料与现在管理员的资料一致

(2) 创建新用户

(3) 复制数据库计划并协同修改

(4) 移动并合成数据库

(5) 删除用户

(6) 导出用户,这样项目组成员可以分发离线数据库备份。

(7) 导入用户,这样项目组成员可以拥有数据库备份,导入数据库,创建新计划。

(8) 导出基线,将主数据库进行备份,这是导出用户的一个特例。

(9) 创建不同计划的报告,以便比较。

(10) 将计划与主计划作比较,这样开发人员就可以将他们本地拷贝与主数据库作比较

(11) 列出所有的用户

分析人员和质量评价人员常常会去看测试数据,并且需要改变他们,因此我们用VBA语句开发一个Excel应用程序,从数据库里面提取数据到Excel文件中,允许用户修改这个文件,修改后又返回到数据库中去。当然,也可以使用其他工具来浏览和编辑数据库的内容,但是我们使用excel,因为很多人熟悉它。

项目组的所有成员应该很容易获取数据库设计的详细内容,从而发现什么表格可以获得,以及如何使用这些表格。我们建立了基于HTML的工具,使用servlets来查询数据库元数据。因此开发人员在添加字段之前,可以先通过搜索表和字段的元数据来看一看数据库中有没有这个字段。我们使用Erwin建模,将数据从Erwin提取到我们的元数据表中。

6 结束语

当然,这并不是敏捷方法在数据库设计中的全部应用,也不是数据库进化设计的全部,还有集成数据库和24*7小时实施以及其他一些没有解决的问题,数据库进化设计还需要进行进一步的研究工作。
支持