第 5 章 版本库管理

Subversion版本库是保存任意数量项目版本化数据的中央仓库,因此,版本库成为管理员关注的对象。版本库的维护一般并不需要太多的关注,但为了避免一些潜在的问题和解决一些实际问题,理解怎样适当的配置和维护还是非常重要的。

在这一章里,我们将讨论如何建立和配置一个Subversion版本库,还会讨论版本库的维护,包括svnlooksvnadmin工具的使用(它们都包含在Subversion中)。我们将说明一些常见的问题和错误,并提供一些安排版本库数据的建议。

如果您只是以普通用户的身份访问版本库对数据进行版本控制(就是说通过Subversion客户端),您完全可以跳过本章。但是如果您已经是或打算成为Subversion版本库的管理员,[13]您一定要关注一下本章的内容。

在进入版本库管理这块宽广的主题之前,让我们进一步确定一下版本库的定义,它是怎样工作的?让人有什么感觉?它希望茶是热的还是冰的,加糖或柠檬吗?作为一名管理员,你应该既从逻辑视角-数据在版本库中如何展示,又能从物理具体细节的视角-版本库如何响应一个非Subversion的工具,来理解版本库的组成。下面的小节从一个比较高的层面覆盖了这些基本概念。

从概念上来说,Subversion的版本库就是一串目录树。每一个目录树,就是版本库的文件和目录在某一时刻的快照。这些快照是客户端使用者操作的结果,叫做修订版本。

每一个修订版本都是以事务树开始其生命周期。做提交操作时,客户端建立了一个映射本地修改的Subversion事务(加上客户端提交操作后任何对版本库的更改),然后指导版本库将该树存储为下一个快照。要是提交成功,这个事务就会成为新的修订版本树,并被赋予新的修订版本号。如果因为某些原因提交失败,事务会被销毁,客户端将被通知这个事务失败。

更新的动作也类似这样。客户端建立一个临时的事务树,映射工作文件的状态。然后版本库比较事务树和被请求的修订版本树(通常是最新的,也就是最“年轻”的修订版本树),然后发回消息通知客户端哪些变更需要将拷贝发送到修订版本树。更新完成后,临时事务将被删除。

事务树的使用是对版本库中版本控制文件系统产生永久变更的唯一方法。一个事务的生命周期非常灵活,了解这一点很重要。在更新的情况下,事务只是马上会被销毁的临时树。在提交的情况下,事务会变成固定的修订版本(如果失败的情况下,则会被删除)。在出现错误或bug的情况下,事务可能会被留在版本库中(不会影响任何东西,但是会占据空间)。

理论上,有一天整个流程能够发展到对事务进行更加细密的流程控制。可以想象一个系统,在客户端完成操作,将要保存到版本库中时,每个加到它的事务都变成一个修订版本。这将会使每一个新的提交都可以被别人查看到,也许是主管,也许是质量保证小组,他们可以决定是要接收这个事务成为修订版本,还是放弃它。

在Subversion1.2中,版本库中存储数据有两种方式。一种是在Berkeley DB数据库中存储数据;另一种是使用普通的文件,使用自定义格式。因为Subversion的开发者称版本库为(版本化的)文件系统,他们接受了称后一种存储方式为FSFS[14]的习惯,也就是说,使用本地操作系统文件系统来存储数据的版本化文件的系统。

建立一个版本库时,管理员必须决定使用Berkeley DB还是FSFS。它们各有优缺点,我们将详细描述。这两个中并没有一个是更正式的,访问版本库的程序与采用哪一种实现方式无关。访问程序并不知道版本库如何存储数据,它们只是从版本库的API读取到修订版本和事务树。

表 5.1 “版本库数据存储对照表”从总体上比较了Berkeley DB和FSFS版本库,下一部分将会详细讲述细节。

在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版本库不能放在网络共享文件夹中,尽管Berkeley DB承诺如果按照一套特定规范的话,可以在网络共享上正常运行,但实际上已知的共享类型几乎都不满足这套规范。

最后,因为Berkeley DB的库直接链接到了Subversion中,它对于中断比典型的关系型数据库系统更为敏感。大多数SQL系统,举例来说,有一个主服务进程来协调对数据库表的访问。如果一个访问数据库的程序因为某种原因出现问题,数据库守护进程察觉到连接中断会做一些清理。因为数据库守护进程是唯一访问数据库表的进程,应用程序不需要担心访问许可的冲突。但是,这些情况与Berkeley DB不同。Subversion(和使用Subversion库的程序)直接访问数据库的表,这意味着如果有一个程序崩溃,就会使数据库处于一个暂时的不一致、不可访问的状态。当这种情况发生时,管理员需要让Berkeley DB恢复到一个检查点,这的确有点讨厌。除了崩溃的进程,还有一些情况能让版本库出现异常,比如程序在数据库文件的所有权或访问权限上发生冲突。因为Berkeley DB版本库非常快,并且可以扩展,非常适合使用一个单独的服务进程,通过一个用户来访问—比如Apache的httpdsvnserve(参见第 6 章 配置服务器)—而不是多用户通过file:///svn+ssh://URL的方式多用户访问。如果将Berkeley DB版本库直接用作多用户访问,请先阅读“支持多种版本库访问方法”一节

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

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

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

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

唯一真正对FSFS不利的是相对于Berkeley DB的不成熟,缺乏足够的使用和压力测试,许多关于速度和可扩展性的判断都是建立在良好的猜测之上。在理论上,它承诺会降低管理员新手的门槛并且更加不容易发生问题。在实践中,只有时间可以证明。



[13] 这可能听起来很崇高, 但我们所指的只是那些对管理别人工作拷贝数据之外的神秘领域感兴趣的人。

[14] 读作“fuzz-fuzz”, 如果Jack Repenning说起这个问题。