有Oracle使用经历的用户都会认为表空间非常重要,但对PostgreSQL中的表空间却知之甚少。本文将解释它们是什么,何时有用以及是否应该使用它们。

什么是表空间

本质上,PostgreSQL中的表空间是一个包含数据文件的目录。这些数据文件是表、序列、索引和物化视图等对象背后的存储空间。在PostgreSQL中,每个这样的对象都有自己的数据文件。如果对象很大,它将有几个被称为segments的文件,大小限制为1GB。

PostgreSQL使用操作系统自带的文件系统进行存储。这与Oracle不同,后者实际上实现了自己的“文件系统”。

让我们比较一下这些术语:

Oracle 和 PostgreSQL中的术语

Oracle

PostgreSQL 或 操作系统

tablespace(表空间)

file system(文件系统)

datafile(数据文件)

logical/physical volume(逻辑/物理卷)

segment(段)

all data files of a table(表的所有数据文件)

extent(区)

segment data file(段/数据文件)

默认表空间

每个PostgreSQL数据库集群最初有两个表空间。你可以在psql 中用\db 列出它们:

List of tablespaces
    Name    |  Owner   | Location 
------------+----------+----------
 pg_default | postgres | 
 pg_global  | postgres | 
(2 rows)

这里没有指定位置。是因为它们总是对应于PostgreSQL数据目录的固定子目录:默认表空间(pg_default )是base 子目录,全局表空间(pg_global )是global 子目录。

默认情况下,所有数据文件都存储在默认表空间中。只有某些对象存储在全局表空间中:目录表pg_databasepg_authidpg_tablespacepg_shdepend 及其所有索引。这些是所有数据库共享的唯一目录表。

创建和使用新的表空间

要创建一个新的表空间,首先必须创建一个新目录。不要在PostgreSQL数据目录中创建该目录!

注意,该目录必须属于“postgres”数据库操作系统用户(确切地说,该用户必须具有更改目录权限的权限)。

然后创建表空间:

CREATE TABLESPACE mytbsp LOCATION '/tmp/mytbsp';

要使用表空间,你可以创建一个表或其他包含存储空间的对象:

也可以将创建的表空间设置为默认表空间:
set default_tablespace = pgtbl_test;
create table foo(i int);

CREATE TABLE newtab (
   id  integer NOT NULL,
   val text    NOT NULL
) TABLESPACE mytbsp;
 
ALTER TABLE newtab
   ADD CONSTRAINT newtab_pkey PRIMARY KEY (id)
   USING INDEX TABLESPACE mytbsp;
 
CREATE INDEX newtab_val_idx ON newtab (val)
   TABLESPACE mytbsp;

注意,索引不会自动创建在与表相同的表空间中。

你也可以在表空间中创建一个数据库:

CREATE DATABASE newdb TABLESPACE mytbsp;

然后,您在该数据库中创建的所有对象将自动放在数据库的表空间中。

您可以使用ALTER 命令修改任何对象的表空间。把一个对象移动到另一个表空间将复制数据文件,并且在移动该对象时无法访问它。

备份和表空间

如果对具有表空间的数据库执行文件系统备份,则必须备份所有表空间。您不能备份或恢复单个表空间,也没有类似Oracle的“可传输表空间”。

普通格式的pg_basebackup 将尝试把表空间保存在与数据库服务器相同的位置(-D选项仅指定数据目录的位置)。要将数据从一个表空间备份到另一个位置,你必须使用选项——tablespace-mapping=olddir=newdir 。可以对多个表空间多次使用此选项。

使用表空间使数据库管理更加复杂,因为PG数据目录不再包含所有数据。

什么时候创建表空间?

在绝大多数情况下,不应该在PostgreSQL中创建额外的表空间。特别是,在与数据目录相同的文件系统上创建一个表空间,或者在与另一个表空间相同的文件系统上创建一个表空间,都没有意义。

那么,表空间有哪些优点可以证明管理的复杂性是合理的呢?

  1. 如果您的数据分布在不同设备上的几个文件系统上,那么您可以分配I/O负载。然而,这也可以通过在较低的级别上进行剥离来完成。
  2. 如果磁盘上的空间用完,表空间将为您提供添加更多存储空间的选项。然而,现在通常可以使用某种类型的卷管理器在操作系统级上完成此工作。
  3. 如果要对数据库或表设置大小限制,可以将其放在文件系统的表空间中。
  4. 如果您同时拥有快速、昂贵和缓慢、廉价的存储空间,那么您可以将需要良好性能的对象放在快速存储空间上的单独表空间中。在这种情况下,您应该调整表空间上的seq_page_costrandom_page_costeffecve_io_concurrency 选项,以便将性能特征告诉优化器。
  5. 如果希望在默认表空间之外的其他地方创建临时文件(用于临时表和用于查询处理),可以将temp_tablespaces参数设置为不同的表空间。

如果您运行在虚拟化环境中,使用虚拟化存储,那么除了第三点之外,其他的点都是没有意义的。由于现在几乎所有人都在使用虚拟化,表空间正在成为PostgreSQL越来越不重要的特性。

消除一个旧的的错误的观点

在数据库管理员中流传着一个根深蒂固的神话,即为了获得良好的性能,您应该将表和索引放在不同的磁盘上。

您将听到人们详细阐述为什么索引扫描期间访问模式的特定相互作用会使其在旋转磁盘上有效。但是旋转磁盘正在停止工作,通常只会使用多个并发 SQL 语句来使存储系统饱和,但无论如何,所有此类访问模式都会中断。

这种误解背后的事实是,将I/O负载分散到多个设备上肯定是有益的。如果在操作系统级别使用条带化,与小心放置表和索引相比,将获得更好的分布。

结论

表空间在PostgreSQL中很少相关。抵制创建表空间的诱惑,将所有数据保留在默认表空间中。