锻造坊 - PostgreSQL 9.0

作者:Reuven M. Lerner

如果你想构建一个 Web 应用程序,你需要一些软件。你需要一个服务器操作系统,比如 Linux。你还需要一个服务器端语言和框架。虽然我个人现在使用 Ruby on Rails,但也有很多其他不错的选择。对于客户端交互,你需要使用一个 JavaScript 框架,比如 jQuery。而且,为了存储关于(和为)你的用户的信息,你需要选择一个持久存储机制,也就是数据库。

直到几年前,任何人使用“数据库”这个术语几乎总是指关系型数据库——也就是,基于链接的二维表,可以使用相对标准的 SQL 语言进行查询。当然,还有各种其他类型的数据库,但它们并没有被广泛使用或认真对待。

然而,在过去的几年里,所谓的 NoSQL 数据库的使用和认知度有了巨大的增长。从 Redis 到 MongoDB 到 CouchDB 再到 Hadoop,这些不同的数据库之间并没有太多的共同点,它们都承诺更高的可扩展性和灵活性。

现在,我并不是说这些非关系型数据库不好。相反,我在我自己的项目中使用了其中的几个,发现它们非常适合某些需求。但是对于日常使用,我仍然发现关系型数据库非常合适。而且当我需要一个关系型数据库时,我总是更喜欢使用 PostgreSQL。事实上,当我在开发 Web 应用程序时,PostgreSQL 是默认选项。只有当我发现它不能很好地工作(无论出于何种原因)时,我才会将部分或全部项目切换到使用不同的数据库。

为什么我如此喜欢 PostgreSQL?首先,因为它非常稳定可靠。开发人员几乎痴迷于你存储在数据库中的数据的安全性,并且他们尽最大努力确保数据永远不会被删除或损坏。这并不意味着这种情况永远不会发生,但它们非常罕见。PostgreSQL 不仅试图确保你的数据的安全,而且还为你提供了各种工具来帮助你约束可能存储在数据库中的值,确保存储的任何内容都保证是有效的。

其次,PostgreSQL 提供了丰富的功能,总是让我印象深刻和惊叹。无论是你可以编写服务器端函数的语言数量,还是你可以定义索引的不同方式,巧妙地使用 MVCC(多版本并发控制)来避免写者阻塞读者,还是不断进行的统计分析来为查询优化器提供数据,在超过 15 年的日常使用中,PostgreSQL 都没有让我失望。

每次我使用另一个关系型数据库时,我都会发现自己希望拥有 PostgreSQL 提供的一个或多个功能,或者认为由于其高质量的架构,PostgreSQL 甚至不会进入某些有问题的境地。这并不意味着 PostgreSQL 是完美的,但它的功能组合对我来说非常有用,而且我并不是唯一有这种感觉的人。

本月,我想看看 PostgreSQL 9.0,它于 2010 年末发布。特别是,我想考虑它与其他开源数据库(主要是 MySQL)相比有哪些优势。对于已经使用 PostgreSQL 的人来说,9.0 版本带来了哪些新功能?

我不会描述安装 PostgreSQL,因为所有主要的 Linux 发行版都有可用的版本。下载和编译源代码相对简单——尽管以专用的“postgres”用户而不是 root 用户这样做,会在安装过程中为你省去很多麻烦——但我发现二进制文件对于我的日常需求来说已经足够好了。

背景

PostgreSQL 是一个由多个不同公司的核心开发人员开发的一个开源(BSD 许可)关系型数据库。(事实上,核心开发团队的规则禁止超过少数几个核心开发人员为同一家公司工作,以确保没有一个组织直接控制开发。)它源于 Michael Stonebreaker 在加州大学伯克利分校开发的 Postgres 项目。Postgres 本身就是对 Stonebreaker 开发和商业化的原始 Ingres 数据库的改进尝试。

Postgres 使用了自己的查询语言,称为 QUEL,并有许多先进的想法,包括许多来自面向对象编程领域。几位开发人员联手采用 Postgres 数据库,用 SQL 替换 QUEL,稳定代码并以开源许可证发布。PostgreSQL 的第一个版本,以其新的名称,于 1996 年发布。非正式地,大量的 PostgreSQL 用户和开发人员继续称其为 Postgres,尽管这在技术上指的是 Stonebreaker 在伯克利的项目,而不是当前的实现。

PostgreSQL 团队的主要目标之一是在可能的情况下遵守 SQL 标准。此外,正如我之前提到的,PostgreSQL 开发人员以其坚如磐石的实现而自豪,在所有情况下都使用标准的 ACID(原子性-一致性-隔离性-持久性)范例进行数据库存储。他们进一步试图在强大的功能集和可配置选项与压倒那些不想成为全职数据库管理员的人之间取得平衡。

所有 PostgreSQL 存储都是事务性的,使用一种称为 MVCC(多版本并发控制)的系统。MVCC 也用于 Oracle 和 CouchDB,它在很大程度上避免了读者和写者之间的冲突,确保在大多数情况下行和表都不需要锁定。MVCC 确实有将“死行”保留在数据库中的副作用,偶尔需要“清理”,类似于许多编程语言中的垃圾回收。多年来,清理需要手动完成,通常通过运行vacuum从 cron 作业运行的命令。现在,autovacuum 守护进程在后台运行,当添加或更新的行达到可配置的阈值时,将空间标记为可重用。

Vacuum 也可以在“分析”模式下运行,在这种模式下,它会检查表和索引,更新在计划查询时使用的统计信息。这是 PostgreSQL 能够如此高效地运行的原因之一,即使在处理复杂的查询时也是如此。通过保持每个表中存储的最新数据描述,它可以就如何优化查询做出明智的决定。如果猜测结果对于你的特定需求来说是错误的,你可以配置一些配置参数。

PostgreSQL 提供了对外键的强大支持,可以确保表之间的连接存在并以正确的方式工作。也就是说,你可以定义一个 People 表,然后定义一个引用它的 Addresses 表

CREATE TABLE People (
id SERIAL,
first_name TEXT,
last_name TEXT,
email_address TEXT,
PRIMARY KEY(id)
);

CREATE TABLE Addresses (
id SERIAL,
person_id INTEGER REFERENCES People,
address TEXT,
PRIMARY KEY(id)
);

现在,让我们尝试在 Addresses 表中插入一条新记录,而首先不在 People 表中插入任何内容

INSERT INTO Addresses (person_id) VALUES (5);

这会导致以下结果

ERROR:  insert or update on table "addresses" violates foreign key
 ↪constraint "addresses_person_id_fkey"
DETAIL:  Key (person_id)=(5) is not present in table "people".

因为我已将 person_id 定义为外键,所以 PostgreSQL 不允许我在那里使用无效值。如果 Addresses 表中有行引用 People 表,PostgreSQL 也会拒绝你从 People 表中删除记录。你可以使用 CASCADE 关键字覆盖这些限制,但未经你的明确批准,数据库永远不会自行删除内容。

当然,你也可以确保你的 People 表中的电子邮件地址是唯一的

CREATE UNIQUE INDEX people_email_idx ON People(email_address);

但是等等,如果有人输入一个大写的电子邮件地址,然后输入同一个小写的电子邮件地址呢?你可以利用我最喜欢的 PostgreSQL 功能之一——函数索引来确保唯一性

CREATE UNIQUE INDEX people_email_idx ON 
 ↪People(lower(email_address));

现在 PostgreSQL 将确保其唯一性,无论大小写如何。如果你试图索引包含长文本字符串的列,此功能也会派上用场。你只能索引前 1,000 个字符

CREATE UNIQUE INDEX people_email_idx ON 
 ↪People(substring(email_address, 1, 1000));

最后,另一个强大的功能是 CTE(公共表表达式)。如果你经常发现自己执行一个查询,将结果放入一个临时表中,然后查询该临时表,那么 CTE 几乎肯定是你想要使用的东西。你基本上描述了临时表查询,使用“with”语句给它一个名称,然后查询该伪表。例如

WITH Mini_users
AS (SELECT id * 2 AS id2, email FROM Users)
SELECT id2 FROM Mini_users;

在上面的例子中,我创建了一个名为 mini_users 的新临时表并定义它,然后查询它,就好像它已经存在一样。我已经发现 CTE 在简化复杂查询方面非常有用。PostgreSQL 还使得定义递归 CTE 变得容易,允许你使用一个简单的查询处理分层数据。

高级功能

多年来我一直喜欢的一个功能是能够用各种语言编写自己的服务器端函数——类似于其他数据库中的“存储过程”。大多数时候,我使用内置的 Pl/PgSQL 语言,它将 SQL 查询与过程语言结合在一起。但是在需要更多功能或者想要使用现有库的情况下,我可以用 PL/Perl、PL/Python 或其他一些语言编写函数。无论我使用哪种语言,这些函数都可以无缝地集成到数据库中,与内置函数的工作方式没有区别,并且允许我在数据库内部处理数据,而不是在我的应用程序中处理数据。

随着时间的推移,这些服务器端语言变得越来越复杂,尽管 Pl/PgSQL 语法不是最新或最具表现力的,但它易于学习,并且可以轻松快速地处理大量情况。我看到了服务器端函数的广泛用途,从将大多数应用程序逻辑封装在这些函数中到处理在逻辑上不属于应用程序层的特定数据操作例程。

我最喜欢的功能之一是在触发器中使用函数——数据库在特定操作发生时自动执行的操作。 例如,我曾经在一个项目中工作,该项目要求我们插入一个URL,但我们也希望有一个(单独的)列包含该URL的主机名。 我编写了一个函数,该函数使用正则表达式来检索主机名,然后自动将主机名插入到相应的列中。 从应用程序的角度来看,它能够插入URL,然后检索URL或主机名。 触发器可用于在对表执行插入、删除或更新查询之前或之后执行各种操作。

9.0版本中的众多进步之一是对“窗口函数”的改进,窗口函数是8.4版本中引入的函数,它使得可以创建对表中其他行的部分进行聚合(例如求和和平均值),而不是对整个表进行聚合。 因此,您可以计算某人的工资与该人所在部门的其他工资之间的差异,或者对一个部门在书目索引中被引用的次数进行排名,或者在MVC Web应用程序中找到每个控制器中最长的运行动作。 我才刚开始使用窗口函数,但是8.4版本中的内置功能,加上9.0版本中增加的一些功能,使得可以轻松查看前一行和后一行,这让我相信这是一个特别强大的功能。

PostgreSQL 9.0

上述功能都是不错的改进,但在全面升级到9.0版本的原因方面,它们只是锦上添花。

首先,此版本使得升级相当简单。 早期版本需要将数据库转储到磁盘,升级服务器,然后从其转储形式恢复数据库。 对于可以承受一段时间离线的小型数据库来说,这可能不是一个大问题,但对于无法承受如此长时间停机的较大型站点来说,这是一个主要问题。

PostgreSQL核心开发人员听取了批评,并以pg_upgrade作为回应。 现在,pg_upgrade仍然被认为是有些实验性的,并且尚未获得开发团队的正式认可,因此它被放置在contrib目录中,而不是在任何官方位置。 但是,pg_upgrade允许您在无需转储或恢复的情况下升级PostgreSQL服务器,这无疑是9.0的主要创新和成就之一,并且受到了以前过于紧张或忙碌而无法升级的人们的热烈欢迎。

另一个主要功能——也许是此版本最令人印象深刻的成就——是所谓的“热流复制”。 此功能实际上是两个不同功能的组合,它们协同工作,形成一个惊人的新备份和高可用性系统。

基本思路如下。 当您将事务提交到PostgreSQL时,它不会立即更新磁盘上的表。 而是将事务记录写入单独的“预写日志”(write-ahead log),或WAL,描述应如何对数据库进行更改。 只有在积累了足够的WAL之后,PostgreSQL才会实际更新数据。

从8.4版本开始,您可以将WAL从一台PostgreSQL服务器复制到另一台服务器,通常使用诸如rsync之类的程序。 当WAL出现在第二台服务器上时,它们将被读取到该系统中。 如果第一台服务器出现故障,则可以将第二台服务器置于活动模式,从第一台服务器停止的地方继续运行。

尽管这总比没有好,但它至少存在两个问题。 不仅通过“日志传送”传输WAL远非理想的机制,而且辅助服务器在接收来自主服务器的数据时无法使用。

PostgreSQL 9.0中已经消除了这两个问题,从而提供了一种健壮且易于配置的主从机制,用于实现高可用性。 现在可以通过网络将WAL从主服务器传输到辅助服务器,方法是将辅助服务器配置为一种特殊的客户端程序。 随着每个事务的发生,WAL可以通过网络发送到一个或多个远程服务器,从而使它们几乎可以与主服务器立即同步。 另一个改进是,即使辅助服务器正在接收来自主服务器的数据,它也可以响应只读查询。

热流复制,作为这两个功能的组合,并不能涵盖所有情况。 例如,我最近正在与一家公司合作,该公司拥有多个PostgreSQL服务器,希望将其数据放到辅助服务器上。 对于此类目的,热流复制将不起作用。 幸运的是,还有其他解决方案,例如Slony,可能更适合公司的需求。 但是,对于许多希望确保其服务器不会宕机的站点来说,此解决方案绰绰有余。

结论

我在滔滔不绝吗? 是的,绝对是。 很少有软件能给我如此愉悦的使用体验,并且让我感觉可以完全依赖,而PostgreSQL是其中的精选成员。 它可以安全地保存我的数据,为我提供大量功能,使我可以以任何我想要和需要的方式扩展数据库,并且以适度的投入时间给我带来出色的性能。 如果您正在考虑切换到开源数据库,或者如果您已经在使用一种数据库并想尝试一些功能强大但仍然易于使用的东西,那么您最好尝试PostgreSQL。 尤其是版本9.0中添加的最新功能,我确信您会找到喜欢的东西。

资源

PostgreSQL的主页是www.postgresql.org。 从该站点,您可以下载软件,订阅一些(流量很大且非常友好的)电子邮件列表,并阅读文档。 Freenode上还有一个#postgresql IRC频道,通常有人在线并可以回答问题。

EnterpriseDB是最著名的商业PostgreSQL支持和产品公司之一,在其网站enterprisedb.com上为许多操作系统提供二进制软件包。 它还提供许多视频和音频播客和截屏视频,包括PostgreSQL核心贡献者Bruce Momjian的视频,质量很高。

最后,Packt Press最近出版了两本关于PostgreSQL的优秀书籍,填补了自Korry Douglas几年前撰写优秀的(但过时的)PostgreSQL, 2nd edition以来的漫长空白。 这两本新书是Simon Riggs和Hannu Krosing的PostgreSQL 9 Administration Cookbook,以及Gregory Smith的PostgreSQL 9.0 High Performance。 我从这两本书中学到了很多,尽管后一本书在硬件上花费的时间比我希望的要多一些,但它也教会了我,即使像我这样的软件工程师有时也需要考虑软件和数据库设计的物理方面。 如果您经常使用PostgreSQL,我强烈推荐这些书。

Reuven M. Lerner是一位长期的Web开发人员、架构师和培训师。 他是西北大学学习科学的博士候选人,研究协作在线社区的设计和分析。 Reuven与他的妻子和三个孩子住在以色列的Modi'in。

加载Disqus评论