本文都是关于一个最近最成功的开源产品,一个名叫Postgresql的关系数据库。
数据库开发商和开源开发者都是Postgresql的热心拥护者。任何使用程序管理大量数据的人都可以从数据库中获得大量益处。Postgresql数据库是一个非常优秀的关系数据库实现,全功能,开源且免费使用。
Postgresql 数据库支持大量的主流开发语言,包括C,C++,Perl,Python,Java,Tcl以及PHP。它是最接近工业标准sql92的查询语言,并且正在实现新的功能以兼容最新的sql标准:sql2003。Postgresql也获得数个奖项,包括三次被评为Linux Journal杂志编辑选择奖最佳数据库(2000,2003和2004年度)以及2004年度Linux新媒体奖最佳数据库系统。我们也许我们有点超越自我。你也许想知道到底Postgresql是什么,为什么你要使用它。
本章我们将设置场景为本书的剩余部分并提供一些关于数据库的概括性背景知识、不同类型的数据库、为什么他们非常有用以及Postgresql在哪些地方符合这些要求。
基于数据编程
基本上所有的非普通计算机程序操作大量的数据,而且大量的应用程序被开发用来处理数据而不是进行计算任务。有些人估计当今世界上80%的应用程序开发会通过某种方式连接到数据库中的复杂数据,所以数据库对于很多应用来说是一种非常重要的基础。
以数据编程的资源很丰富。大部分好的编程书籍都包含章节关于建立、存储和操作数据。有三本书(由Wrox发布)包含以数据编程的信息:
l 《Beginning Linux Programming》第三版覆盖了DBM库以及MysqL数据库系统。
l 《Professional Linux Programming》包含关于Postgresql和MysqL数据库系统的章节。
l 《Beginning Databases with MysqL》全面覆盖了MysqL数据库系统。
静态数据
数据有很多形态和大小,我们根据数据的不同性质处理数据。有些情况下,数据非常简单——也许仅仅是一个数字例如π的数值存在程序中用来画圆。程序本身可能包含这个硬编码的圆周率的值。我们把这种数据叫做静态数据,它永远不需要改变。
另一种静态数据的例子是欧洲一些国家的汇率。在欧元区,货币都按固定的保留小数点后6位的精度兑换到欧元。假设我们开发一个欧元区货币转换程序。他可能有一个硬编码的货币名称和基本汇率表,保存对应到欧元的兑换点数。这些汇率永远不会改变。不过还没完,因为这张表会随着欧元区国家数量的增长而增长。当一个国家注册到欧元区,这个国家货币对欧元的汇率将被设定,它将需要被加入这张表中。当这种情况发生,汇率转换程序需要修改,内置的货币表被修改,程序需要重建。这需要在每次货币表发生变化时做一次。
一个更好的办法是应用程序读取一个包含简单的货币信息的文件,文件可能包含货币的名称、国际符号以及汇率。之后在这个表需要改变时我们仅仅需要改变 这个文件,而不需要改变程序。
我们使用的这个数据文件没有特别的结构;它仅仅包含几行文本,包含一些数据让应用程序读取。它没有固定的结构。因此我们把这种文件叫做扁平文件。以下是我们的货币文件可能的样子:
France FRF 6.559570
Germany DEM 1.955830
Italy ITL 1936.270020
Belgium BEF 40.339901
用于数据存储的扁平文件
扁平文件对于很多应用类型来说非常有用。由于文件保持在可管理大小,我们很容易改变它,扁平文件可能非常符合我们的需求。
很多系统和程序,特别是UNIX平台,使用扁平文件存储数据或者进行数据交换。UNIX的密码文件就是一个例子,它通常情况下像以下的样子:
neil:*:500:100:Neil Matthew:/home/neil:/bin/bash
nick:*:501:100:Rick Stones:/home/rick:/bin/bash
这个例子包含一系列的信息和属性组成的记录。文件被设计成每行包含一个记录,整个文件则用来保存相关的记录到一起。某些情形下,这种模式还不够用,因而,我们需要增加扩展的功能来配合应用程序完成必须的工作。
重复单元和其他问题
假设我们决定扩展货币汇率程序(本章前面介绍过)来记录每个国家的语言,以及人口和面积。在扁平文件中,我们需要每行一个记录,每个记录由几个熟悉构成。记录中每个独立的属性总在固定的位置;例如货币符号总是第二个熟悉。所以我们可以认为按列读数据,每列总是相同类型的信息。
为了添加某个国家使用的语言,我们可能会认为我们只需为每一行要添加一个新列。当我们发现一些国家有不止一种官方语言时,我们碰到了一个问题。所以,在我们给Belgium的记录中,我们需要同时包含Flemish语和French语。对于Switzerland,我们需要添加四种语言。扁平文件在这是应该看起来像这样:
France FRF 6.559570 French 60424213 547030
Germany DEM 1.955830 German 82424609 357021
Italy ITL 1936.270020 Italian 58057477 301230
Belgium BEF 40.339901 Flemish French 10348276 30528
Switzerland CHF 1.5255 German French Italian Romansch 7450867 41290
这个问题被称为重复单元。我们现在的问题是重复的数据项(语言)是在记录内重复,也就是说数据可能会在列内重复,而不仅仅是记录。扁平的文件无法应付这类问题,因为它无法判断什么时候语言项列完了,开始下一个记录了。解决这个问题唯一的方法是在文件内添加一些结构,但这种情况下这个文件不再是扁平文件了。
这里有另一个示例。记录DVD详细信息的程序可能需要记录生产的年份,导演,风格以及演员列表。我们可以设计一个和Windows的.ini文件格式差不多的文件,就像这样:
[2001: A Space Odyssey]
year=1968
director=Stanley Kubrick
genre=science fiction
starring=Keir Dullea
starring=Leonard Rossiter
…
[Toy Story]
…
我们通过引入描述每个记录类型的标记解决了重复单元的问题。但是,现在你的程序为了获得需要的数据,需要读取和解析更复杂的文件。添加记录以及搜索这种结构就更困难了。我们怎样才能确保用于风格或分类的描述是从一个特定子集选择的。我们怎样才能很容易的搜索到Kubrick导演的电影?
由于对数据的需求越来越复杂,我们被迫写越来越多的代码用来读取和存储我们的数据。如果我们扩展我们的DVD管理程序,为DVD出租店主包含一些有用的信息——例如会员信息,租金,退租信息和预定信息,在扁平文件中管理所有这些信息的希望渺茫。
另外一个通用问题就是大小的问题。虽然包含结构的文本文件可以通过暴力扫描回答复杂的查询类似于“告诉我所有的最近三个月租过最少一次喜剧的会员”,不单代码很难编写,而且性能可能会很低。这是因为程序除了扫描整个文件来查找任何一片信息外没有别的办法,即使是仅仅返回一条数据的问题“谁主演了2001年的A Space Odyssey”。
我们需要的是一种在通用数据处理系统里用来存储和检索数据的方法,而不是总是发明一个个解决方案用于有点不同但非常相似的问题。