版本库开发策略

因为Subversion版本库本身和所依赖技术设计的简单性,创建和配置版本库是一件相对直接的任务。需要做一些的预备决定,但是设置Subversion版本库的实际工作非常直接,在做过几次之后就会发现不必费太多心思去做这件事。

下面是一些你需要预先考虑的事情:

在本节,我们要尝试帮你回答这些问题。

规划你的版本库结构

在Subversion版本库中,移动版本化的文件和目录不会损失任何信息,甚至也可以将版本库的的一组数据无损历史的移植到另一个版本库,但是这样一来那些经常访问版本库并且以为文件总是在同一个路径的用户可能会受到干扰。为将来着想,最好预先对你的版本库布局进行规划。以一种高效的“布局”开始项目,可以减少将来很多不必要的麻烦。

假如你是一个版本库管理员,需要向多个项目提供版本控制支持。那么,你首先要决定的是,用一个版本库支持多个项目,还是为每个项目建立一个版本库,还是两种方法的混合方式。

使用一个版本库支持多个项目有很多好处,最明显的无过于不需要维护好几个版本库。单一版本库就意味着只有一个钩子程序,只需要备份一个数据库,当Subversion进行不兼容升级时,只需要一次转储和装载操作,等等。还有,你可以轻易的在项目之间移动数据,还不会损失任何历史版本信息。

单一版本库的缺点是,不同的项目通常都有不同的版本库触发事件需求,例如需要发送提交通知邮件到不同的邮件列表,需要不同的鉴定提交是否合法的定义。这些都不是不可逾越的问题,当然—之需要你的钩子程序能够察看版本库的布局,而不是假定整个版本库与同一组人关联。还有,别忘了Subversion的修订版本号是针对整个版本库的,这些号码没有任何魔力。即使最近没有对某个项目作出修改,版本库的修订版本号还是会因为其它项目的修改而不停的提升,许多人并不喜欢这样的事实。[25]

可以采用折中的办法。比如,可以把许多项目按照彼此之间的关联程度划分为几个组合,然后为每一个项目组合建立一个版本库。这样,在相关项目之间共享数据依旧很简单,而如果修订版本号有了变化,至少开发人员知道,改变的东西多少和他们有些关系。

在决定了如何用版本库组织项目以后,就该决定如何设置版本库的目录层次了。由于Subversion按普通的目录复制方式完成分支和标签操作(参见第 4 章 分支与合并),Subversion社区建议为每一个项目建立一个项目根目录—项目的“顶级”目录—然后在根目录下建立三个子目录:trunk,保存项目的开发主线;branches,保存项目的各种开发分支;tags,保存项目的标签,也就是创建后永远不会修改的分支(可能会删除)。 [26]

举个例子,一个版本库可能会有如下的布局:

/
   calc/
      trunk/
      tags/
      branches/
   calendar/
      trunk/
      tags/
      branches/
   spreadsheet/
      trunk/
      tags/
      branches/
   …

项目在版本库中的根目录地址并不重要。如果每个版本库中只有一个项目,那么就可以认为项目的根目录就是版本库的根目录。如果版本库中包含多个项目,那么可以将这些项目划分成不同的组合(按照项目的目标或者是否需要共享代码甚至是字母顺序)保存在不同子目录中,下面的例子给出了一个类似的布局:

/
   utils/
      calc/
         trunk/
         tags/
         branches/
      calendar/
         trunk/
         tags/
         branches/
      …
   office/
      spreadsheet/
         trunk/
         tags/
         branches/
      …

按照你认为合适的方式安排版本库的布局,Subversion自身并不强制或者偏好某一种布局形式,对于Subversion来说,目录就是目录。最后,在设计版本库布局的时候,不要忘了考虑一下项目参与者们的意见。

为了完整性,我们需要提一下另一种常见的布局,在这种布局中trunktagsbranches都在根目录下,而你的项目在各个子目录下,例如:

/
   trunk/
      calc/
      calendar/
      spreadsheet/
      …
   tags/
      calc/
      calendar/
      spreadsheet/
      …
   branches/
      calc/
      calendar/
      spreadsheet/
      …

这种布局没有什么不对的,但是它只是或不是你的用户的直觉。特别是在大的,有许多用户的多项目情况下,用户可能只熟悉版本库中一两个项目。但是项目作为分支的方式可以鼓励项目的个性和将注意力集中在一个单独的实体。尽管这也是一个社会问题,因为实践的原因,我们很愿意对安排提出一些建议—当一个项目的历史都在一个目录里时,很容易查询(或是修改、移植)单个项目的历史—过去、现在、标签和分支—单独为那个项目。

决定在哪里与如何部署你的版本库

在创建Subversion版本库之前,一个明显的问题是所有的东西要存放在什么地方,这与很多问题关联,包括版本库如何访问(通过Subversion服务器或直接访问)、被谁访问(防火墙后的用户或全部是在Internet上)、你将围绕Subversion提供哪些服务(版本库浏览接口,e-mail为基础的提交通知等)、你的数据备份策略,等等。

我们在第 6 章 服务配置覆盖了服务器的选择和配置,我们也提供一些可能会使你必须决定使用某种服务器的问题的答案。例如,特定的部署策略可能会需要从多个计算机通过远程文件系统访问版本库,这个情况下(下一小节会读到)要求你不能选择一种版本库后端数据存储方式,因为只有一种后端在这种场景下可以工作。

列出所有的Subversion可能的部署方法是不可能的,超出了本书的范围,我们只是简单的鼓励你使用这部分内容和参考材料验证你的想法,并往前计划。

选择数据存储格式

在Subversion1.1中,版本库中有两种数据存储方式—通常叫做“后端”或其他容易混淆的名字,如“(版本化的) 文件系统”,每一个版本库都会使用一种。一种是在Berkeley DB数据库中存储数据,我们称之为“BDB后端”;另一种是使用普通的文件,自定义格式,Subversion开发者根据习惯称之为FSFS[27] —一种使用本地操作系统文件存储数据的版本化文件系统直接实现—而不是通过某个数据库层或其他抽象层来保存数据。

表 5.1 “”从总体上比较了Berkeley DB和FSFS版本库。

表 5.1. 

分类特性Berkeley DBFSFS
可靠性数据完整性当正确部署,非常可靠;Berkeley DB 4.4支持自动恢复较老的版本较少被描述,但是有数据毁坏bug
对操作中断的敏感很敏感;系统崩溃或者权限问题会导致数据库“塞住”,需要定期进行恢复。十分敏感
可用性可只读加载不能可以
存储平台无关不能可以
可从网络文件系统访问通常,不可以
组访问权处理对于用户的umask设置十分敏感,最好只由一个用户访问。对umask设置不敏感
伸缩性版本库磁盘使用情况较大(特别是没有清除日志时)较小
修订版本树的数量数据库,没有限制许多古老的本地文件系统在处理单一目录包含上千个条目时出现问题。
有很多文件的目录较慢较快
性能检出最新的代码没有有意义的区别没有有意义的区别
大的提交整体较慢,但是在整个提交周期中消耗被分摊较快,但是最后较长的延时可能会导致客户端操作超时

两种后端都有优点和缺点,没有一种更加“正式”,尽管新的FSFS在Subversion1.2成为缺省数据存储,两者用来存储版本化数据都是可靠的。但是就象你在表 5.1 “”看到的,FSFS后端在部署场景中提供了更多的灵活性,更灵活意味着你很难错误的配置。那些原因—加上不使用Berkeley DB意味着在这个系统有更少的组件—这就是为什么今天几乎所有的人都使用FSFS来创建新的版本库。

幸运的是,大多数访问Subversion的程序不会在意其所用的后端数据存储。而且你不必一定要使用你最初的数据存储方法—如果后来你改变了主意,Subversion提供了移植版本库数据到另一个版本库的方法,我们会在后面详细讨论。

下面的小节提供了数据存储类型更加详细的介绍。

Berkeley DB

在Subversion的初始设计阶段,开发者因为多种原因而决定采用Berkeley DB,比如它的开源协议、事务支持、可靠性、性能、简单的API、线程安全、支持游标等。

Berkeley DB提供了真正的事务支持-这或许是它最强大的特性,访问你的Subversion版本库的多个进程不必担心偶尔会破坏其他进程的数据。事务系统提供的隔离对于任何给定的操作,Subversion版本库代码看到的只是数据库的静态视图-而不是一个在其他进程影响不断变化的数据库-并能够根据该视图作出决定。如果该决定正好同其他进程所做操作冲突,整个操作会回滚,就像什么都没有发生一样,并且Subversion会优雅的再次对更新的静态视图进行操作。

Berkeley DB另一个强大的特性是热备份-不必“脱机”就可以备份数据库环境的能力。我们将会在“版本库备份”一节讨论如何备份你的版本库,能够不停止系统对版本库做全面备份的好处是显而易见的。

Berkeley DB同时是一个可信赖的数据库系统。Subversion利用了Berkeley DB可以记日志的便利,这意味着数据库先在磁盘上写一个日志文件,描述它将要做的修改,然后再做这些修改。这是为了确保如果如果任何地方出了差错,数据库系统能恢复到先前的检查点—一个日志文件认为没有错误的位置,重新开始事务直到数据恢复为一个可用的状态。关于Berkeley DB日志文件的更多信息请查看“管理磁盘空间”一节

但是每朵玫瑰都有刺,我们也必须记录一些Berkeley DB已知的缺陷。首先,Berkeley DB环境不是跨平台的。你不能简单的拷贝一个在Unix上创建的Subversion版本库到一个Windows系统并期望它能够正常工作。尽管Berkeley DB数据库的大部分格式是不受架构约束的,但环境还是有一些方面没有独立出来。其次,使用Berkeley DB的Subversion不能在95/98系统上运行—如果你需要将版本库建在一个Windows机器上,请装到Windows2000或WindowsXP上。

然而Berkeley DB对于在网络共享上工作提出了一组规范,[28]大多数网络文件系统和应用没有实现这个要求,所以不能允许在网络共享上的BDB后端版本库被多个客户端同时访问(首先要知道版本库存放在网络共享上是非常普遍的)。

警告

如果你尝试在不顺从的远程文件系统上使用Berkeley DB,结果是不可预知的—你会立刻看到神秘的错误,或者是在发生隐含错误之后几个月之后才发现。你必须认真考虑在网络共享情况下使用FSFS数据存储。

最后,因为Berkeley DB的库直接链接到了Subversion中,它对于中断比典型的关系型数据库系统更为敏感。大多数SQL系统,举例来说,有一个主服务进程来协调对数据库表的访问。如果一个访问数据库的程序因为某种原因出现问题,数据库守护进程察觉到连接中断会做一些清理。因为数据库守护进程是唯一访问数据库表的进程,应用程序不需要担心访问许可的冲突。但是,这些情况与Berkeley DB不同。Subversion(和使用Subversion库的程序)直接访问数据库的表,这意味着如果有一个程序崩溃,就会使数据库处于一个暂时的不一致、不可访问的状态。当这种情况发生时,管理员需要让Berkeley DB恢复到一个检查点,这的确有点讨厌。除了崩溃的进程,还有一些情况能让版本库出现异常,比如程序在数据库文件的所有权或访问权限上发生冲突。

注意

Berkeley DB 4.4(对应Subversion 1.4和更高)提供了在需要恢复时自动恢复Berkeley DB环境的能力,当Subversion进程发现任何以前进程未清理的连接,就会执行所有可能的恢复,然后就当什么都没有发生一样继续执行。这样不会完全消除版本库楔住的可能,但是大大减少了人工干预恢复的数量。

因为Berkeley DB是这样快速和可伸缩,最好是使用某种单用户单服务进程方式处理—例如Apache的httpdsvnserve(见第 6 章 服务配置)—而最好不要使用许多不同的用户通过file://svn+ssh://的URL访问的方法。如果使用多个用户直接访问Berkeley DB版本库的,请确定要读“支持多种版本库访问方法”一节

FSFS

在2004年中期,另一种版本库存储系统慢慢形成了:一种不需要数据库的存储系统。FSFS版本库在单一文件中存储修订版本树,所以版本库中所有的修订版本都在一个子文件夹中有限的几个文件里。事务在单独的子目录中被创建,创建完成后,一个单独的事务文件被创建并移动到修订版本目录,这保证提交是原子性的。因为一个修订版本文件是持久不可改变的,版本库也可以做到“”备份,就象Berkeley DB版本库一样。

修订版本文件格式代表了一个修订版本的目录结构,文件内容,和其它修订版本树中相关信息。不像Berkeley DB数据库,这种存储格式可跨平台并且与CPU架构无关。因为没有日志或用到共享内存的文件,数据库能被网络文件系统安全的访问和在只读环境下检查。缺少数据库花消同时也意味着版本库的总体体积可以稍小一点。

FSFS也有一种不同的性能特性。当提交大量文件时,FSFS可以更快的追加条目。另一方面,FSFS通过写入与上一个版本比较的变化来记录新版本,这也意味着获取最新修订版本时会比Berkeley DB慢一点,提交时FSFS也会有一个更长的延迟,在某些极端情况下会导致客护端在等待回应时超时。

最重要的区别是当出现错误时FSFS不会楔住的能力。如果使用Berkeley DB的进程发生许可错误或突然崩溃,数据库会一直无法使用,直到管理员恢复。假如在应用FSFS版本库时发生同样的情况,版本库不会受到任何干扰,最坏情况下也就是会留下一些事务数据。

FSFS的唯一真实的争议是其相对于Berkeley DB的不成熟,不像Berkeley DB有着多年历史的,而且有专门的开发团队,强大的Oracle会提供支持。[29]FSFS在工程上更新一点,在Subversion1.4之前,我们还未一些确实很严重的数据一致性问题颤抖,尽管只在非常罕见的情况下发生,然而还是发生了。但是,FSFS还是很快被一些最大的开放和私有Subversion版本库所采用,并且承诺了在跨平台时的有较少的麻烦。



[25] 无论是在忽略情况下建立或很少考虑过如何产生正确的软件开发矩阵,都不应该愚蠢的担心全局的修订版本号码,这不应该成为安排项目和版本库的理由。

[26] trunktagsbranches可以使用“TTB目录”来表示。

[27] 通常读作“fuzz-fuzz”, 如果Jack Repenning说起这个问题。(本书,假定读者认为是“eff-ess-eff-ess”。)

[28] Berkeley DB需要底层的文件系统实现严格的POSIX锁定语法,更重要的是,将文件直接映射到内存的能力。

[29] Oracle在2006情人节购买了Sleepycat和它的旗舰软件Berkeley DB。