#### 5.3 Constraints
数据类型是一种限制可以存储在表中的数据类型的方法。 但是,对于许多应用程序,它们提供的约束过于粗糙。 例如,包含产品价格的列可能只应接受正值。 但是没有标准的数据类型只接受正数。 另一个问题是,您可能希望相对于其他列或行约束列数据。 例如,在包含产品信息的表中,每个产品编号应只有一行
为此,SQL允许您定义列和表的约束。 约束使您可以根据需要尽可能多地控制表中的数据。 如果用户尝试将数据存储在违反约束的列中,则会引发错误。 即使该值来自默认值定义,也是如此。
##### 5.3.1 Check Constraints
检查约束是最通用的约束类型。 它允许您指定某个列中的值必须满足布尔值(真值)表达式。 例如,要使产品价格为正数,可以使用:
```sql
CREATE TABLE products (
product_no integer,
name text,
price numeric CHECK (price > 0)
);
```
如您所见,约束定义位于数据类型之后,就像默认值定义一样。 默认值和约束可以任何顺序列出。 检查约束由关键字CHECK以及括号中的表达式组成。 检查约束表达式应包含受约束的列,否则约束不会有太大意义。
您还可以为约束指定一个单独的名称。 这样可以澄清错误消息,并允许您在需要更改约束时引用约束。 语法为:
```sql
CREATE TABLE products (
product_no integer,
name text,
price numeric CONSTRAINT positive_price CHECK (price > 0)
);
```
因此,要指定命名约束,请使用关键字CONSTRAINT,后跟标识符和约束定义。 (如果您未以这种方式指定约束名称,则系统会为您选择一个名称。)
检查约束也可以引用几列。 假设您存储正常价格和折扣价格,并且要确保折扣价格低于正常价格:
```sql
CREATE TABLE products (
product_no integer,
name text,
price numeric CHECK (price > 0),
discounted_price numeric CHECK (discounted_price > 0),
CHECK (price > discounted_price)
);
```
前两个约束应该看起来很熟悉。 第三个使用新语法。 它没有附加到特定的列,而是在逗号分隔的列列表中显示为单独的项目。 列定义和这些约束定义可以以混合顺序列出。
我们说前两个约束是列约束,而第三个约束是表约束,因为它是与任何一个列定义分开编写的。 列约束也可以写为表约束,而反向约束不一定是可能的,因为列约束应该仅指代其所附的列。 (PostgreSQL并不强制执行该规则,但是如果您希望表定义与其他数据库系统一起使用,则应遵循该规则。)上面的示例也可以写成:
```sql
CREATE TABLE products (
product_no integer,
name text,
price numeric,
CHECK (price > 0),
discounted_price numeric,
CHECK (discounted_price > 0),
CHECK (price > discounted_price)
);
```
或者
```sql
CREATE TABLE products (
product_no integer,
name text,
price numeric CHECK (price > 0),
discounted_price numeric,
CHECK (discounted_price > 0 AND price > discounted_price)
);
```
可以使用与列约束相同的方式将名称分配给表约束:
```sql
CREATE TABLE products (
product_no integer,
name text,
price numeric,
CHECK (price > 0),
discounted_price numeric,
CHECK (discounted_price > 0),
CONSTRAINT valid_discount CHECK (price > discounted_price)
);
```
应当注意,如果校验表达式的值为真或空值,则满足校验约束。 由于大多数表达式在任何操作数为null的情况下都会求值为null,因此它们不会在受约束的列中阻止null值。 为了确保一列不包含空值,可以使用下一节中描述的非空约束。
##### 5.3.2 Not-Null Constraints
非空约束仅指定列不得采用空值。 语法示例:
```sql
CREATE TABLE products (
product_no integer NOT NULL,
name text NOT NULL,
price numeric
);
```
非空约束始终被写为列约束。 非空约束在功能上等同于创建检查约束CHECK(column_name IS NOT NULL),但是在PostgreSQL中,创建显式非空约束更为有效。 缺点是您不能为以此方式创建的非空约束指定显式名称。
当然,一列可以有多个约束。 只需一个接一个地写约束:
```sql
CREATE TABLE products (
product_no integer NOT NULL,
name text NOT NULL,
price numeric NOT NULL CHECK (price > 0)
);
```
顺序无关紧要。 它不一定确定检查约束的顺序。
NOT NULL约束具有相反的含义:NULL约束。 这并不意味着该列必须为空,这肯定是无用的。 相反,这只是选择默认行为,即列可能为空。 NULL约束在SQL标准中不存在,因此不应在可移植应用程序中使用。 (它被添加到PostgreSQL是为了与其他一些数据库系统兼容。)但是,某些用户喜欢它,因为它使切换脚本文件中的约束变得容易。 例如,您可以从以下内容开始:
```sql
CREATE TABLE products (
product_no integer NULL,
name text NULL,
price numeric NULL
);
```
然后在需要的地方插入NOT关键字。在大多数数据库设计中,大多数列应标记为非null
##### 5.3.3 Unique Constraints
唯一性约束可确保一列或一组列中包含的数据在表的所有行中都是唯一的。 语法为:
```sql
CREATE TABLE products (
product_no integer UNIQUE,
name text,
price numeric
);
```
作为列约束编写时,以及:
```sql
CREATE TABLE products (
product_no integer,
name text,
price numeric,
UNIQUE (product_no)
);
```
作为表约束编写时。要为一组列定义唯一约束,请将其写为表约束,并以逗号分隔列名:
```sql
CREATE TABLE example (
a integer,
b integer,
c integer,
UNIQUE (a, c)
);
```
这指定指示列中的值组合在整个表中是唯一的,尽管任何一列都不必(通常不是)唯一。您可以按照通常的方式为唯一的约束分配自己的名称:
```sql
CREATE TABLE products (
product_no integer CONSTRAINT must_be_different UNIQUE,
name text,
price numeric
);
```
添加唯一约束将在约束中列出的列或一组列上自动创建唯一的B树索引。 不能将仅覆盖某些行的唯一性限制写为唯一性约束,但是可以通过创建唯一的部分索引来实施这种限制。
通常,如果表中有多于一行的行,其中约束中包含的所有列的值均相等,则违反唯一约束。 但是,在此比较中,永远不会将两个空值视为相等。 这意味着即使在存在唯一约束的情况下,也可以在至少一个约束列中存储包含空值的重复行。 此行为符合SQL标准,但是我们听说其他SQL数据库可能不遵循此规则。 因此,在开发可移植的应用程序时要小心。
在Postgres-XL中,在分布式表中,唯一约束必须包含表的分布列。 这是因为Postgres-XL当前仅允许其向下推送到Datanodes以便在本地实施。 如果我们将分发列包含在唯一约束中,则可以在本地强制实施。 如果表是由ROUNDROBIN分发的,则我们不能强制执行UNIQUE约束,因为它没有分发列。 一个列的相同值可能存在于多个节点上。 复制表中的UNIQUE约束没有任何限制。 当对UNIQUE约束使用表达式时,该表达式必须包含其父表的distribution列。 它也不能使用其他列。
##### 5.3.4 Primary Keys
主键约束指示一列或一组列可以用作表中行的唯一标识符。 这要求这些值必须是唯一的并且不能为null。 因此,以下两个表定义接受相同的数据:
```sql
CREATE TABLE products (
product_no integer UNIQUE NOT NULL,
name text,
price numeric
);
CREATE TABLE products (
product_no integer PRIMARY KEY,
name text,
price numeric
);
```
主键可以跨越多列; 语法类似于唯一约束:
```sql
CREATE TABLE example (
a integer,
b integer,
c integer,
PRIMARY KEY (a, c)
);
```
添加主键将自动在主键中列出的列或一组列上创建唯一的B树索引,并强制将这些列标记为NOT NULL。
一个表最多可以有一个主键。 (可以有任意数量的唯一约束和非空约束,它们在功能上几乎是同一件事,但是只能将其中一个标识为主键。)关系数据库理论规定,每个表都必须具有一个主键。 PostgreSQL没有强制执行此规则,但是通常最好遵循它。
正如讨论UNIQUE约束时提到的那样,分发列必须包含在PRIMARY KEY中。其他限制也适用于主键。当对PRIMARY KEY约束使用表达式时,该表达式必须包含其父表的distribution列。它也不能使用其他列。
主键对于文档目的和客户端应用程序都是有用的。例如,允许修改行值的GUI应用程序可能需要知道表的主键才能唯一地标识行。如果已经声明了主键,则数据库系统还可以通过多种方式使用主键。例如,主键定义了引用其表的外键的默认目标列。
##### 5.3.5 Foreign Keys
外键约束指定一列(或一组列)中的值必须与另一个表的某一行中出现的值匹配。 我们说这保持了两个相关表之间的参照完整性。
假设您拥有我们已经使用过几次的产品表:
```sql
CREATE TABLE products (
product_no integer PRIMARY KEY,
name text,
price numeric
);
```
我们还假设您有一个表来存储这些产品的订单。 我们要确保订单表仅包含实际存在的产品的订单。 因此,我们在引用产品表的订单表中定义了一个外键约束。
```sql
CREATE TABLE orders (
order_id integer,
product_no integer REFERENCES products (product_no),
quantity integer
) DISTRIBUTE BY HASH(product_no);
```
请注意,带有REFERENCES的列应该是分布列。 在这种情况下,我们不能将PRIMARY KEY添加到order_id,因为PRIMARY KEY也必须是分发列。 引入此限制是因为约束仅在每个Datanode中本地执行。
现在,无法使用没有出现在产品表中的非NULL product_no条目创建订单。
我们说在这种情况下,订单表是引用表,产品表是引用表。 同样,也有引用和引用列。
您也可以将以上命令缩短为:
```sql
CREATE TABLE orders (
order_id integer PRIMARY KEY,
product_no integer REFERENCES products,
quantity integer
);
```
因为在没有列列表的情况下,被引用表的主键用作被引用列。在Postgres-XL中,您不能在REFERENCES子句中省略列名。外键也可以约束和引用一组列。 通常,它需要以表约束形式编写。 这是一个人为的语法示例:
```sql
CREATE TABLE t1 (
a integer PRIMARY KEY,
b integer,
c integer,
FOREIGN KEY (b, c) REFERENCES other_table (c1, c2)
);
```
当然,约束列的数量和类型需要与引用列的数量和类型匹配。在Postgres-XL中,不能为不同的列同时指定PRIMARY KEY和REFERENCES键。
您可以按照通常的方式为外键约束分配自己的名称。
一个表可以具有多个外键约束。 这用于实现表之间的多对多关系。 假设您有关于产品和订单的表格,但是现在您希望允许一个订单包含可能很多产品(以上结构不允许)。 您可以使用此表结构:
```sql
CREATE TABLE products (
product_no integer PRIMARY KEY,
name text,
price numeric
);
CREATE TABLE orders (
order_id integer PRIMARY KEY,
shipping_address text,
...
);
CREATE TABLE order_items (
product_no integer REFERENCES products,
order_id integer REFERENCES orders,
quantity integer,
PRIMARY KEY (product_no, order_id)
);
```
请注意,主键与最后一个表中的外键重叠。在Postgres-EXCEL中,不能指定多个外键约束。
我们知道,外键不允许创建与任何产品都不相关的订单。 但是,如果在创建引用该产品的订单后将其删除,该怎么办? SQL也允许您处理该问题。 直观地,我们有几种选择:
- 禁止删除引用的产品
- 删除订单
- 还有吗
为了说明这一点,让我们在上面的多对多关系示例中实施以下策略:当某人想要删除仍由订单引用的产品(通过order_items)时,我们将其禁止。 如果有人删除了订单,则订单项也将被删除:
```sql
CREATE TABLE products (
product_no integer PRIMARY KEY,
name text,
price numeric
);
CREATE TABLE orders (
order_id integer PRIMARY KEY,
shipping_address text,
...
);
CREATE TABLE order_items (
product_no integer REFERENCES products ON DELETE RESTRICT,
order_id integer REFERENCES orders ON DELETE CASCADE,
quantity integer,
PRIMARY KEY (product_no, order_id)
)
```
限制删除和级联删除是两个最常见的选项。 RESTRICT防止删除引用的行。 NO ACTION表示如果在检查约束时仍存在任何引用行,则会引发错误;如果您未指定任何内容,则这是默认行为。 (这两种选择之间的本质区别是,NO ACTION允许将支票推迟到事务的后面,而RESTRICT不允许。)CASCADE指定在删除引用的行时,应自动删除引用该行的行也一样还有其他两个选项:SET NULL和SET DEFAULT。当删除引用的行时,这将导致引用行中的引用列分别设置为null或它们的默认值。请注意,这些不能使您免于遵守任何约束条件。例如,如果某个操作指定SET DEFAULT,但默认值不满足外键约束,则该操作将失败。
类似于ON DELETE,当更改(更新)引用的列时,也会调用ON UPDATE。可能的动作是相同的。在这种情况下,CASCADE意味着应将引用列的更新值复制到引用行中。
通常,如果引用行的任何引用列为null,则不必满足外键约束。如果将MATCH FULL添加到外键声明中,则仅当其所有引用列都为null时,引用行才能满足约束条件(因此,确保null和非null值的混合可确保不通过MATCH FULL约束)。如果您不希望引用行能够避免满足外键约束,则将引用列声明为NOT NULL。
外键必须引用作为主键或形成唯一约束的列。这意味着被引用的列始终具有一个索引(作为主键或唯一约束的基础的索引);因此,检查引用行是否有匹配项将非常有效。由于从被引用表中删除一行或对被引用列进行UPDATE将需要对引用表进行扫描以查找与旧值匹配的行,因此通常也建议对引用列进行索引。因为这并不总是需要的,并且在如何建立索引方面有很多选择,所以外键约束的声明不会自动在引用列上创建索引。
有关更新和删除数据的更多信息,请参见第6章。另请参见参考文档中CREATE TABLE的外键约束语法说明。
##### 5.3.6 Exclusion Constraints
排除约束条件确保,如果使用指定的运算符在指定的列或表达式上比较任何两行,则这些运算符比较中的至少一个将返回false或null。 语法为:
```sql
CREATE TABLE circles (
c circle,
EXCLUDE USING gist (c WITH &&)
);
```
有关详细信息,另请参见CREATE TABLE ... CONSTRAINT ... EXCLUDE。
添加排除约束将自动创建约束声明中指定类型的索引。**Postgres-XL不支持排除约束。**
#### 5.4 System Columns
每个表都有几个系统隐式定义的系统列。 因此,这些名称不能用作用户定义列的名称。 (请注意,这些限制与名称是否是关键字是分开的;引用名称将使您无法逃避这些限制。)您实际上并不需要担心这些列;您不必担心这些限制。 只知道它们存在。
oid
行的对象标识符(对象ID)。 仅当表是使用WITH OIDS创建的,或者当时已设置default_with_oids配置变量时,才显示此列。 此列是oid类型(与列名称相同); 有关类型的更多信息,请参见第8.18节。
请注意,Postgres-XL不会在群集之间强制执行OID完整性。 OID在每个协调器和数据节点中本地分配。 您可以在表达式中使用它,但不要期望整个XL群集的OID值都相同。
tableoid
包含该行的表的OID。 对于从继承层次结构中选择的查询,此列特别方便(请参见5.9节),因为如果没有它,则很难确定一行来自哪个表。 可以将tableoid与pg_class的oid列连接起来以获得表名。
请注意,Postgres-XL不会在群集之间强制执行OID完整性。 OID在每个协调器和数据节点中本地分配。 您可以在表达式中使用它,但不要期望整个XL群集中的OID值都相同。
xmin
此行版本的插入事务的标识(事务ID)。 (行版本是行的单独状态;行的每次更新都会为同一逻辑行创建一个新的行版本。)
cmin
插入事务中的命令标识符(从零开始)。
xmax
删除事务的标识(事务ID),对于未删除的行版本为零。 在可见行版本中,此列可能为非零。 这通常表明删除事务尚未提交,或者尝试删除的操作已回滚。
cmax
删除事务中的命令标识符,或者为零。
ctid
行版本在其表中的物理位置。 请注意,尽管可以使用ctid快速定位行版本,但是如果通过VACUUM FULL更新或移动了行的ctid,则该行的ctid将会更改。 因此,ctid不能用作长期行标识符。 应该使用OID或更好的用户定义序列号来标识逻辑行。
在Postgres-XL中,ctid对于协调器和数据节点是本地的。 在SQL语句中使用此值不是一个好习惯,并且在使用它更新数据时可能非常危险。
OID为32位,并从单个群集范围的计数器分配。 在大型或长期存在的数据库中,计数器可能会回绕。 因此,除非您采取措施确保确实如此,否则认为OID是唯一的是不好的做法。 如果需要识别表中的行,强烈建议使用序列生成器。 但是,只要采取一些其他预防措施,也可以使用OID:
- 应该在每个表的OID列上创建一个唯一约束,将使用该OID来标识行。 当存在这样的唯一约束(或唯一索引)时,系统注意不要生成与现有行匹配的OID。 (当然,只有在表包含少于232(40亿)行的情况下才有可能,并且实际上表的大小最好远小于该行,否则性能可能会受到影响。)
- OID绝对不应在表之间是唯一的; 如果需要数据库范围的标识符,请使用tableoid和行OID的组合。
- 当然,有问题的表必须使用OIDS创建。 从PostgreSQL 8.1开始,缺省为WITHOUT OIDS。
事务标识符也是32位的数量。 在寿命长的数据库中,事务ID可能会回绕。 如果采取适当的维护程序,这不是致命的问题; 有关详细信息,请参见第24章。 但是,长期依赖事务ID(超过十亿笔交易)的唯一性是不明智的。
命令标识符也是32位量。 这在单个事务中创建了232(40亿)条SQL命令的硬限制。 实际上,此限制不是问题-请注意,此限制是针对SQL命令的数目,而不是处理的行数。 同样,只有实际修改数据库内容的命令才会使用命令标识符。
#### 5.5 Modifying Tables
当您创建表并意识到自己犯了一个错误或应用程序的要求发生变化时,可以删除该表并重新创建它。 但这不是一个方便的选择,如果该表已被数据填充,或者该表已被其他数据库对象引用(例如,外键约束)。 因此,PostgreSQL提供了一系列命令来修改现有表。 请注意,从概念上讲,这与更改表中包含的数据不同:在这里,我们有兴趣更改表的定义或结构。
你可以做如下操作:
- 添加列
- 删除列
- 添加约束
- 删除约束
- 修改默认值
- 修改数据类型
- 修改列名
- 修改表名
所有这些操作都是使用ALTER TABLE命令执行的,该命令的参考页包含此处未提供的详细信息。
除了这些操作之外,在Postgres-XL中,您还可以使用ALTER TABLE命令执行以下操作:
- 修改表的分布式策略
- 添加节点到已有的分布式节点中
- 从已有的分布式节点中移除节点
- 如果现有值在新数据类型的范围内,则修改分布列定义
在Postgres-XL中,不允许以下操作:
- 修改分布列值
- 删除分布式字段
##### 5.5.1 Adding a Column
给表添加一个列,可以使用如下命令:
```sql
ALTER TABLE products ADD COLUMN description text;
```
最初,新列将填充给定的默认值(如果未指定DEFAULT子句,则为null)。
您还可以使用通常的语法同时在列上定义约束:
```sql
ALTER TABLE products ADD COLUMN description text CHECK (description <> '');
```
实际上,可以在此处使用可以应用于CREATE TABLE中的列描述的所有选项。 但是请记住,默认值必须满足给定的约束,否则ADD将失败。 另外,您可以在正确填写新列之后再添加约束(见下文)。
添加具有默认值的列需要更新表的每一行(以存储新的列值)。 但是,如果未指定默认值,则PostgreSQL可以避免物理更新。 因此,如果您打算用非默认值填充该列,则最好添加无默认值的列,使用UPDATE插入正确的值,然后添加任何所需的默认值,如下所述。
##### 5.5.2 Removing a Column
要删除列,请使用类似以下的命令:
```sql
ALTER TABLE products DROP COLUMN description;
```
列中的所有数据都会消失。 涉及该列的表约束也被删除。 但是,如果该列被另一个表的外键约束引用,则PostgreSQL将不会默默地删除该约束。 您可以通过添加CASCADE授权删除所有依赖于该列的内容:
```sql
ALTER TABLE products DROP COLUMN description CASCADE;
```
请参阅第5.13节,以获取有关其背后的一般机制的描述。
##### 5.5.3 Adding a Constraint
要添加约束,请使用表约束语法。 例如:
```sql
ALTER TABLE products ADD CHECK (name <> '');
ALTER TABLE products ADD CONSTRAINT some_name UNIQUE (product_no);
ALTER TABLE products ADD FOREIGN KEY (product_group_id) REFERENCES product_groups;
```
要添加不能写为表约束的非空约束,请使用以下语法:
```sql
ALTER TABLE products ALTER COLUMN product_no SET NOT NULL;
```
约束将被立即检查,因此表数据必须满足约束才能添加。
请记住,分布式列必须包含在UNIQUE和REFERENCE约束中。
##### 5.5.4 Removing a Constraint
要删除约束,您需要知道其名称。 如果您给它起个名字,那很容易。 否则,系统会分配一个生成的名称,您需要找出该名称。 psql命令\ d表名在这里可能会有所帮助; 其他接口也可能提供检查表详细信息的方法。 然后命令是:
```sql
ALTER TABLE products DROP CONSTRAINT some_name;
```
(如果要处理生成的约束名称,例如$ 2,请不要忘记将其双引号使之成为有效的标识符。)
与删除列一样,如果要删除其他依赖项的约束,则需要添加CASCADE。 一个示例是外键约束取决于引用列上的唯一键或主键约束。
除非空约束以外,所有约束类型的工作原理都相同。 要删除非空约束,请使用:
```sql
ALTER TABLE products ALTER COLUMN product_no DROP NOT NULL;
```
(请记住,非空约束没有名称。)
##### 5.5.5 Changing a Column's Default Value
要为列设置新的默认值,请使用以下命令:
```sql
ALTER TABLE products ALTER COLUMN price SET DEFAULT 7.77;
```
请注意,这不会影响表中的任何现有行,而只是更改以后的INSERT命令的默认值。
要删除任何默认值,请使用:
```sql
ALTER TABLE products ALTER COLUMN price DROP DEFAULT;
```
这实际上与将默认值设置为null相同。 因此,在未定义默认值的地方删除默认值不是错误,因为默认值隐式为空值。
##### 5.5.6 Changing a Column's Data Type
要将列转换为其他数据类型,请使用类似以下命令:
```sql
ALTER TABLE products ALTER COLUMN price TYPE numeric(10,2);
```
仅当可以通过隐式强制转换将列中的每个现有条目转换为新类型时,此操作才会成功。 如果需要更复杂的转换,则可以添加USING子句,该子句指定如何从旧值计算新值。
PostgreSQL将尝试将列的默认值(如果有)转换为新类型,以及涉及该列的所有约束。 但是这些转换可能会失败,或者会产生令人惊讶的结果。 通常最好在更改其类型之前先在列上放下任何约束,然后再添加适当修改后的约束。
##### 5.5.7 Renaming a Column
给列重命名,使用如下命令:
```sql
ALTER TABLE products RENAME COLUMN product_no TO product_number;
```
##### 5.5.8 Renaming a Table
给表重命名命令如下:
```sql
ALTER TABLE products RENAME TO items;
```
##### 5.5.9 Adding a Node to Distribution Target
要将新节点添加到已有分布式中:
```sql
ALTER TABLE products ADD NODE (datanode_3);
```
数据节点必须是有效的数据节点名称。 如果它是一个新的数据节点,则必须通过适当的机制将其添加到群集中。
此命令将重新分发现有的表数据,以包括新的节点。 例如,在使用DISTRIBUTE BY HASH策略的情况下,必须重新计算每行的哈希值,并且必须基于分发列表中新的数据节点数将行移动到所选节点。 此操作需要对表进行排他锁定,因此对表的所有读取和写入访问都将被暂时阻止。 这在表很大且需要大量时间进行重新分配的情况下尤其重要。
##### 5.5.10 Removing a Node from Distribution Target
要从现有分布式中删除节点:
```sql
ALTER TABLE products DELETE NODE (datanode_3);
```
必须注意,如果要从群集中删除节点,重要的是首先使用该节点从所有现有表中删除该节点,然后再从群集中删除该节点。
像前面的命令一样,这也需要对表进行排他锁,从而阻止对表的所有读取和写入访问。
##### 5.5.11 Changing Distribution Strategy
要更改表的分配策略:
```sql
ALTER TABLE products DISSTRIBUTE BY REPLICATION;
```
您可以使用此命令更改表的分配策略。 例如,以前由HASH分发的表现在可以由REPLICATION分发。
像前面的命令一样,这也需要对表进行排他锁,从而阻止对表的所有读取和写入访问。
#### 5.6 Privileges
创建对象后,将为其分配一个所有者。 所有者通常是执行创建语句的角色。 对于大多数对象,初始状态是只有所有者(或超级用户)才能对对象执行任何操作。 要允许其他角色使用它,必须授予特权。
有不同类型的特权:SELECT,INSERT,UPDATE,DELETE,TRUNCATE,REFERENCES,TRIGGER,CREATE,CONNECT,TEMPORARY,EXECUTE和USAGE。 适用于特定对象的特权因对象的类型(表,函数等)而异。 有关PostgreSQL支持的不同特权类型的完整信息,请参见GRANT参考页面。 以下各节和各章还将向您展示如何使用这些特权。
修改或销毁对象的权利始终仅是所有者的特权。
可以使用与该对象适当类型的ALTER命令将一个对象分配给新所有者。 更改表。 超级用户可以随时执行此操作; 如果普通角色既是对象的当前所有者(或所有者角色的成员)又是新所有者角色的成员,则它们只能这样做。
要分配特权,请使用GRANT命令。 例如,如果joe是现有角色,accounts是现有表,则可以使用以下方式授予更新表的特权:
```sql
GRANT UPDATE ON accounts TO joe;
```
写ALL代替特定特权将授予与对象类型相关的所有特权。
特殊的“角色”名称PUBLIC可用于为系统上的每个角色授予特权。 此外,当数据库的用户很多时,可以设置“组”角色来帮助管理特权-有关详细信息,请参见第21章。
要撤消特权,请使用适当命名的REVOKE命令:
```sql
REVOKE ALL ON accounts FROM PUBLIC;
```
对象所有者的特殊特权(即执行DROP,GRANT,REVOKE等的权限)始终隐含在所有者中,不能被授予或撤销。 但是对象所有者可以选择撤消自己的普通特权,例如使表对自己和其他人都是只读的。
通常,只有对象的所有者(或超级用户)才能授予或撤销对象的特权。 但是,可以“使用授予选项”授予特权,这使接收者有权依次将其授予其他人。 如果随后取消了授予选项,则所有从该接收者(直接或通过一系列授予)获得特权的人都将失去该特权。 有关详细信息,请参见GRANT和REVOKE参考页。
#### 5.7 Row Security Policies
#### 5.8 Schemas
PostgreSQL数据库集群包含一个或多个命名数据库。 用户和用户组在整个集群中共享,但是没有其他数据在数据库中共享。 与服务器的任何给定客户端连接都只能访问单个数据库(连接请求中指定的数据库)中的数据。
集群的用户不一定具有访问集群中每个数据库的特权。 共享用户名意味着在同一集群中的两个数据库中不能有不同的用户,例如joe。 但是系统可以配置为仅允许joe访问某些数据库。
数据库包含一个或多个命名模式,而这些命名模式又包含表。 模式还包含其他种类的命名对象,包括数据类型,函数和运算符。 相同的对象名称可以在不同的模式中使用,而不会发生冲突。 例如,schema1和myschema都可以包含名为mytable的表。 与数据库不同,架构不是严格分开的:用户可以访问与其连接的数据库中任何架构中的对象(如果有权限)。
人们可能要使用模式的原因有几个:
- 允许许多用户使用一个数据库而不会互相干扰。
- 将数据库对象组织成逻辑组以使其更易于管理。
- 第三方应用程序可以放在单独的架构中,这样它们就不会与其他对象的名称冲突。
模式类似于操作系统级别的目录,不同之处在于架构不能嵌套。
##### 5.8.1 Creating a Schema
要创建模式,请使用CREATE SCHEMA命令。 给模式一个您选择的名称。 例如:
```sql
CREATE SCHEMA myschema;
```
要在模式中创建或访问对象,请写一个限定名称,该名称由模式名称和表名称组成,并用点号分隔:
```
schema.table
```
这适用于需要表名的任何地方,包括表修改命令和以下章节中讨论的数据访问命令。 (为简便起见,我们仅讨论表,但是相同的思想也适用于其他类型的命名对象,例如类型和函数。)
实际上,更通用的语法
```
database.schema.table
```
也可以使用,但是目前这仅仅是为了符合SQL标准的形式。 如果编写数据库名称,则该名称必须与连接的数据库名称相同。
因此,要在新模式中创建表,请使用:
```sql
CREATE TABLE myschema.mytable (
...
);
```
要删除模式为空(已删除其中的所有对象)的模式,请使用:
```sql
DROP SCHEMA myschema;
```
要删除包含所有包含的对象的模式,请使用:
```sql
DROP SCHEMA myschema CASCADE;
```
请参阅第5.13节,以获取有关其背后的一般机制的描述。
通常,您将需要创建一个其他人拥有的模式(因为这是将用户的活动限制在定义明确的名称空间中的一种方法)。 其语法为:
```sql
CREATE SCHEMA schema_name AUTHORIZATION user_name;
```
您甚至可以省略架构名称,在这种情况下,模式名称将与用户名相同。 有关如何使用的信息,请参见第5.8.6节。
以pg_开头的模式名称仅供系统使用,不能由我们创建
##### 5.8.2 The Public Schema
在前面的部分中,我们创建了没有指定任何模式名称的表。 默认情况下,此类表(和其他对象)会自动放入名为“ public”的模式中。 每个新数据库都包含这样的模式。 因此,以下内容等效:
```sql
CREATE TABLE products ( ... )
等效于
CREATE TABLE public.products ( ... )
```
##### 5.8.3 The Schema Search Path
合格名称的编写很繁琐,而且通常最好不要将任何特定的模式名称连接到应用程序中。 因此,表通常由不合格的名称引用,该名称仅由表名称组成。 系统通过遵循搜索路径(这是要查找的模式列表)来确定要使用哪个表。搜索路径中的第一个匹配表被视为需要的表。 如果搜索路径中没有匹配项,即使数据库中其他模式中存在匹配的表名,也会报告错误。
在不同模式中创建名称相同的对象的能力使编写每次都引用完全相同的对象的查询变得复杂。 它还为用户打开了恶意或意外更改其他用户查询行为的可能性。 由于查询中普遍存在不合格名称,并且在PostgreSQL内部使用了不合格名称,因此将模式添加到search_path可以有效地信任对该模式具有CREATE特权的所有用户。 当您运行普通查询时,能够在搜索路径的架构中创建对象的恶意用户可以控制并执行任意SQL函数,就像执行它们一样。
在搜索路径中命名的第一个模式称为当前模式。 除了作为第一个搜索的模式之外,如果CREATE TABLE命令未指定模式名称,它也是将在其中创建新表的模式。
要显示当前搜索路径,请使用以下命令:
```
SHOW search_path;
```
在默认设置中,将返回:
```
search_path
--------------
"$user", public
```
第一个元素指定要搜索与当前用户同名的模式。 如果不存在这样的模式,则该条目将被忽略。 第二个元素指的是我们已经看到的public模式。
搜索路径中存在的第一个模式是用于创建新对象的默认位置。 这就是默认情况下在public模式中创建对象的原因。 如果在没有模式限定条件(表修改,数据修改或查询命令)的任何其他上下文中引用对象,则遍历搜索路径,直到找到匹配的对象。 因此,在默认配置中,任何不合格的访问都只能再次引用public模式。
要将新模式放在路径中,我们使用:
```
SET search_path TO myschema,public;
```
(我们在这里省略了$ user,因为我们不需要它。)然后我们可以在没有模式限定的情况下访问该表:
```
DROP TABLE mytable;
```
另外,由于myschema是路径中的第一个元素,因此默认情况下将在其中创建新对象。
我们也可以写:
```
SET search_path TO myschema;
```
这样一来,如果没有明确的限制,我们将无法再访问公共模式。 除了默认存在之外,公共模式没有什么特别的。 也可以将其删除。
另请参见第9.25节,以了解其他操作模式搜索路径的方法。
搜索路径对数据类型名称,函数名称和运算符的作用与表名称的作用相同。 数据类型和函数名称的限定方式可以与表名称完全相同。 如果需要在表达式中写一个合格的运算符名称,则有一条特殊规定:您必须编写
OPERATOR(schema.operator)
这是避免语法歧义所必需的。 一个例子是:
```
SELECT 3 OPERATOR(pg_catalog.+) 4;
```
实际上,人们通常依赖于运算符的搜索路径,以免写出那么丑陋的东西。
##### 5.8.4 Schemas and Privileges
默认情况下,用户无法访问他们不拥有的模式中的任何对象。 为此,架构的所有者必须在模式上授予USAGE特权。 为了允许用户使用模式中的对象,可能需要授予其他特权,以适合该对象。
还可以允许用户在其他人的模式中创建对象。 为此,需要授予模式的CREATE特权。 请注意,默认情况下,每个人都对模式public具有CREATE和USAGE特权。 这允许所有能够连接到给定数据库的用户在其公共模式中创建对象。 一些使用模式要求撤销该特权:
```
REVOKE CREATE ON SCHEMA public FROM PUBLIC;
```
(第一个“ public”是模式,第二个“ public”表示“每个用户”。从第一个意义上讲,它是一个标识符,在第二个意义上,它是一个关键字,因此大小写不同;请回顾本节的准则 4.1.1。)
##### 5.8.5 The System Catalog Schema
除了公共和用户创建的模式之外,每个数据库还包含pg_catalog模式,该模式包含系统表以及所有内置数据类型,函数和运算符。 pg_catalog始终有效地是搜索路径的一部分。 如果未在路径中明确命名,则在搜索路径的架构之前将对其进行隐式搜索。 这样可以确保始终可以找到内置名称。 但是,如果您希望用户定义的名称覆盖内置名称,则可以在搜索路径的末尾显式放置pg_catalog。
由于系统表名称以pg_开头,因此最好避免使用此类名称,以确保在将来的某个版本中定义与该表相同名称的系统表时不会发生冲突。 (使用默认搜索路径,然后会将对表名的不合格引用解析为系统表。)系统表将继续遵循以pg_开头的名称的约定,以便它们不会与不合格的用户冲突。 表名称,只要用户避免使用pg_前缀即可。
##### 5.8.6 Usage Patterns
模式可以以多种方式用于组织数据。 默认配置很容易支持几种使用模式,当数据库用户不信任其他数据库用户时,只有其中一种就足够了:
- 将普通用户限制为用户专用模式。 要实现此目的,请REVOKE CREATE ON SCHEMA public FROM PUBLIC,然后为每个与该用户同名的用户创建一个模式。 如果受影响的用户之前已登录,请考虑审核公共模式中名为pg_catalog中的对象的对象。 回想一下,默认搜索路径以$ user开头,解析为用户名。 因此,如果每个用户都有一个单独的架构,则默认情况下他们将访问自己的架构。
- 使用 ALTER ROLE user SET search_path = "$user"从每个用户的默认搜索路径中删除公共模式。 每个人都保留在公共架构中创建对象的能力,但是只有合格的名称才能选择这些对象。 尽管合格的表引用很好,但在公共模式中对函数的调用将是不安全或不可靠的。 同样,拥有CREATEROLE特权的用户可以撤消此设置,并可以在依赖该设置的用户身份下发出任意查询。 如果您在公共模式中创建函数或扩展,或者将CREATEROLE授予不保证具有这种几乎超级用户能力的用户,请改用第一种模式。
- 从postgresql.conf中的search_path中删除公共模式。 随后的用户体验与以前的模式匹配。 除了该模式对函数和CREATEROLE的影响外,它还信任CREATEROLE之类的数据库所有者。 如果您在公共架构中创建函数或扩展,或者将CREATEROLE特权,CREATEDB特权或单个数据库所有权分配给不保证几乎具有超级用户访问权限的用户,请改用第一种模式。
- 保留默认值。 所有用户都隐式访问公共模式。 这模拟了模式根本不可用的情况,从而使从非模式感知的世界平稳过渡。 但是,任何用户都可以在不选择单独保护自己的任何用户的身份下发出任意查询。 仅当数据库有一个用户或几个相互信任的用户时,此模式才可接受。
对于任何模式,要安装共享应用程序(每个人都将使用的表,第三方提供的其他功能等),请将它们置于单独的模式中。 记住要授予适当的特权,以允许其他用户访问它们。 然后,用户可以通过使用模式名称来限定名称来引用这些其他对象,或者他们可以根据需要将其他模式放入搜索路径中。
##### 5.8.7 Portability
在SQL标准中,不存在由不同用户拥有的同一模式中的对象的概念。 此外,某些实现不允许您创建名称与其所有者不同的架构。 实际上,在仅实现标准中指定的基本模式支持的数据库系统中,模式和用户的概念几乎等效。 因此,许多用户认为限定名称实际上由user_name.table_name组成。 如果您为每个用户创建每个用户的架构,这就是PostgreSQL的有效行为。
另外,SQL标准中没有公共模式的概念。 为了最大程度地符合标准,您不应使用公共模式。
当然,某些SQL数据库系统可能根本不实现模式,或者可能通过允许(可能是有限的)跨数据库访问来提供名称空间支持。 如果您需要使用这些系统,则完全不使用模式即可实现最大的可移植性。
#### 5.9 Inhertiance
PostgreSQL实现了表继承,这对于数据库设计者而言可能是一个有用的工具。 (SQL:1999及更高版本定义了类型继承功能,该功能在许多方面与此处描述的功能有所不同。)
让我们从一个示例开始:假设我们正在尝试为城市构建数据模型。 每个州都有许多城市,但只有一个首都。 我们希望能够快速检索任何特定州的首都。 这可以通过创建两个表来完成,一个表用于州首府,一个表用于非首府城市。 但是,当我们要查询有关城市的数据时,无论它是否是首都,会发生什么? 继承功能可以帮助解决此问题。 我们定义大写表格,使其继承自城市:
```sql
CREATE TABLE cities (
name text,
population float,
altitude int -- in feet
);
CREATE TABLE capitals (
state char(2)
) INHERITS (cities);
```
在这种情况下, 州首府表继承了其父表格城市的所有列。 州首府也有一个额外的栏,州,显示其州。
在PostgreSQL中,一个表可以继承零个或多个其他表,而查询可以引用一个表的所有行或该表的所有行及其所有后代表。 后一种行为是默认行为。 例如,以下查询查找海拔超过500英尺的所有城市(包括州首府)的名称:
```sql
SELECT name, altitude
FROM cities
WHERE altitude > 500;
```
给定PostgreSQL教程的示例数据(请参阅第2.1节),将返回:
```
name | altitude
-----------+----------
Las Vegas | 2174
Mariposa | 1953
Madison | 845
```
另一方面,以下查询将查找非州首府且海拔超过500英尺的所有城市:
```
SELECT name, altitude
FROM ONLY cities
WHERE altitude > 500;
name | altitude
-----------+----------
Las Vegas | 2174
Mariposa | 1953
```
此处的ONLY关键字指示该查询仅适用于城市,**而不适用于继承层次结构中城市以下的任何表**。 我们已经讨论过的许多命令(SELECT,UPDATE和DELETE)都支持ONLY关键字。
**您还可以在表名后面加上*,以明确指定包含后代表:**
```sql
SELECT name, altitude
FROM cities*
WHERE altitude > 500;
```
不必写*,因为此行为始终是默认行为。 但是,仍支持此语法,以便与可以更改默认值的较早版本兼容。
在某些情况下,您可能希望知道特定行来自哪个表。 每个表中都有一个名为tableoid的系统列,它可以告诉您原始表:
```sql
SELECT c.tableoid, c.name, c.altitude
FROM cities c
WHERE c.altitude > 500;
```
返回值为:
```
tableoid | name | altitude
----------+-----------+----------
139793 | Las Vegas | 2174
139793 | Mariposa | 1953
139798 | Madison | 845
```
(如果尝试重现此示例,则可能会获得不同的数字OID。)通过与pg_class进行联接,您可以看到实际的表名:
```sql
SELECT p.relname, c.name, c.altitude
FROM cities c, pg_class p
WHERE c.altitude > 500 AND c.tableoid = p.oid;
```
返回值如下:
```
relname | name | altitude
----------+-----------+----------
cities | Las Vegas | 2174
cities | Mariposa | 1953
capitals | Madison | 845
```
获得相同效果的另一种方法是使用regclass别名类型,该类型将象征性地打印表OID:
```sql
SELECT c.tableoid::regclass, c.name, c.altitude
FROM cities c
WHERE c.altitude > 500;
```
继承不会自动将数据从INSERT或COPY命令传播到继承层次结构中的其他表。 在我们的示例中,以下INSERT语句将失败:
```sql
INSERT INTO cities (name, population, altitude, state)
VALUES ('Albany', NULL, NULL, 'NY');
```
我们可能希望将数据以某种方式路由到capitals表,但这不会发生:INSERT总是完全插入到指定的表中。 在某些情况下,可以使用规则重定向插入(请参见第40章)。 但是,这对上述情况没有帮助,因为citys表不包含列状态,因此在应用规则之前,该命令将被拒绝。
父表上的所有检查约束和非空约束都将由其子表自动继承,除非用NO INHERIT子句明确指定。 其他类型的约束(唯一,主键和外键约束)不会被继承。
一个表可以从多个父表中继承,在这种情况下,它具有父表定义的列的并集。 子表的定义中声明的所有列都将添加到这些列中。 如果同一列名出现在多个父表中,或者出现在父表和子表的定义中,则这些列将被“合并”,因此子表中只有一个这样的列。 要合并,列必须具有相同的数据类型,否则会引发错误。 可继承的检查约束和非空约束以类似的方式合并。 因此,例如,如果合并列来自的任何一个列定义都标记为非空,则该合并列将被标记为非空。 如果检查约束具有相同的名称,则合并它们;如果它们的条件不同,则合并将失败。
表继承通常是在创建子表时使用CREATE TABLE语句的INHERITS子句建立的。 或者,可以使用ALTER TABLE的INHERIT变体,以兼容方式定义的表可以添加新的父关系。 为此,新的子表必须已经包含名称和类型与父表相同的列。 它还必须包括与父项具有相同名称和校验表达式的校验约束。 同样,可以使用ALTER TABLE的NO INHERIT变体从子级中删除继承链接。 当将继承关系用于表分区时,像这样动态添加和删除继承链接会很有用(请参阅第5.10节)。
创建兼容表(以后将成为新子表)的一种便捷方法是使用CREATE TABLE中的LIKE子句。 这将创建一个与源表具有相同列的新表。 如果在源表上定义了任何CHECK约束,则应指定LIKE的INCLUDING CONSTRAINTS选项,因为新子级必须具有与父级匹配的约束,才被认为是兼容的。
在保留任何子表的同时,不能删除父表。 如果子表是从任何父表继承的,则子表的列或检查约束都不能删除或更改。 如果要删除表及其所有后代,一种简单的方法是使用CASCADE选项删除父表(请参阅第5.13节)。
ALTER TABLE将传播列数据定义中的所有更改,并沿继承层次结构检查约束。 同样,仅当使用CASCADE选项时,才可以删除其他表所依赖的列。 对于在CREATE TABLE期间应用的重复列合并和拒绝,ALTER TABLE遵循相同的规则。
继承的查询仅对父表执行访问权限检查。 因此,例如,授予对城市表的UPDATE权限意味着当通过城市访问首都表中的行时,也允许对其进行更新。 这样可以保留数据也位于父表中的格式 。 但是,capitals表不能直接更新。 以类似的方式,在继承查询期间,父表的行安全策略(请参见5.7节)应用于子表中的行。 子表的策略(如果有)仅在查询中显式命名的表时才应用; 在这种情况下,将忽略附加到其父级的任何策略。
就像常规表一样,外部表(请参阅第5.11节)也可以作为父表或子表作为继承层次结构的一部分。 如果外部表是继承层次结构的一部分,则整个层次结构也不支持外部表不支持的任何操作。
##### 5.9.1 Caveats
请注意,并非所有的SQL命令都可以在继承层次结构上工作。 通常,用于数据查询,数据修改或架构修改的命令(例如SELECT,UPDATE,DELETE,ALTER TABLE的大多数变体,而不是INSERT或ALTER TABLE ... RENAME)大多数情况下默认包含子表并仅支持 表示法将它们排除在外。 执行数据库维护和调整的命令(例如REINDEX,VACUUM)通常仅适用于单个物理表,并且不支持递归继承层次结构。 每个单独命令的各自行为记录在其参考页(SQL命令)中。
继承功能的一个严重限制是索引(包括唯一约束)和外键约束仅适用于单个表,不适用于它们的继承子级。 在外键约束的引用和引用方面均是如此。 因此,就以上示例而言:
- 如果我们将city.name声明为UNIQUE或PRIMARY KEY,这不会阻止capitals表的行名称与城市中的行重复。 默认情况下,这些重复的行将显示在城市查询中。 实际上,默认情况下,大写字母根本没有唯一的约束,因此可以包含多个具有相同名称的行。 您可以向首都添加唯一的约束,但是与城市相比,这不能防止重复。
- 同样,如果我们要指定city.name参考其他表,则此约束将不会自动传播到首都。 在这种情况下,您可以通过将相同的REFERENCES约束手动添加到capitals来解决此问题。
- 指定另一个表的列REFERENCES city(name)将允许另一个表包含城市名称,但不能包含大写名称。 在这种情况下,没有好的解决方法。
这些缺陷可能会在将来的某些版本中修复,但是与此同时,在确定继承是否对您的应用程序有用时,需要格外小心。
#### 5.10 Table Partitioning
##### 5.10.1 Overview
##### 5.10.2 Declarative Partitioning
##### 5.10.3 Implementation Using Inheritance
##### 5.10.4 Partitioning and Constraint Exclusion
#### 5.11 Foreign Data
#### 5.12 Other Database Objects
#### 5.13 Dependency Tracking
### Data Manipulation
### Queries
### Data Types
### Functions and Operators
### Type Conversion
### Indexes
### Full Text Search
### Concurrency Control
### 14.Performance Tips
查询性能可能受许多因素影响。 其中一些可以由用户控制,而其他一些则是系统基础设计的基础。 本章提供一些有关了解和调整PostgreSQL性能的提示。
#### 14.1 Using EXPLAIN
本节中的示例是使用9.3开发资源在进行VACUUM ANALYZE之后从回归测试数据库中提取的。 如果您自己尝试这些示例,则应该能够获得类似的结果,但是您的估计成本和行数可能会略有不同,因为ANALYZE的统计数据是随机样本而不是精确样本,并且成本本质上取决于平台。
这些示例使用EXPLAIN的默认“文本”输出格式,该格式紧凑且便于人类阅读。 如果要将EXPLAIN的输出提供给程序以进行进一步分析,则应改用其机器可读的输出格式之一(XML,JSON或YAML)。
##### 14.1.1 EXPLAIN Basic
查询计划的结构是计划节点的树。树底部的节点是扫描节点:它们从表中返回原始行。对于不同的表访问方法,有不同类型的扫描节点:顺序扫描,索引扫描和位图索引扫描。也有非表行源,例如FROM中的VALUES子句和集返回函数,它们具有自己的扫描节点类型。如果查询要求对原始行进行联接,聚集,排序或其他操作,则扫描节点上方将有其他节点来执行这些操作。同样,通常有不止一种可能的方式来执行这些操作,因此在这里也可能出现不同的节点类型。 EXPLAIN的输出对于计划树中的每个节点都有一行,显示基本节点类型以及计划者为执行该计划节点而进行的成本估算。从节点的摘要行缩进可能会出现其他行,以显示节点的其他属性。第一行(最顶层节点的摘要行)具有该计划的估计总执行成本;计划者设法使这个数字最小化
这是一个简单的示例,只是为了显示输出内容:
```
EXPLAIN SELECT * FROM tenk1;
QUERY PLAN
-------------------------------------------------------------
Seq Scan on tenk1 (cost=0.00..458.00 rows=10000 width=244)
cost: 耗费时间(读第一行耗费时间,读完所有行耗费时间)
rows:所读取行数
width: 行宽度(所占空间字节大小)
```
由于此查询没有WHERE子句,因此它必须扫描表的所有行,因此计划者选择使用简单的顺序扫描计划。 括号中的数字是(从左到右):
- 估计的启动成本。 这是输出阶段可以开始之前花费的时间,例如,在排序节点中进行排序的时间。
- 估计总费用。 这是在假设计划节点已运行完毕(即检索到所有可用行)的假设下得出的。 实际上,节点的父节点可能会停止读取所有可用行(请参阅下面的LIMIT示例)。
- 此计划节点输出的估计行数。 同样,假定该节点运行完毕。
- 此计划节点输出的行的估计平均宽度(以字节为单位)。
成本以由计划者的成本参数确定的任意单位计量(请参阅第19.7.2节)。 传统做法是以磁盘页面提取为单位来衡量成本。 也就是说,seq_page_cost通常设置为1.0,而其他cost参数则相对于此设置。 本节中的示例使用默认的cost参数运行。
了解上级节点的成本包括其所有子节点的成本非常重要。 同样重要的是要意识到,成本仅反映计划者关心的事情。 特别是,成本不考虑将结果行传输给客户端所花费的时间,这可能是实际经过时间的重要因素; 但是计划者会忽略它,因为它无法通过更改计划来更改它。 (我们相信,每个正确的计划都会输出相同的行集。)
行值有点棘手,因为它不是计划节点处理或扫描的行数,而是节点发出的数。 由于被节点上应用的任何WHERE子句条件过滤,结果通常少于扫描的数目。 理想情况下,顶层行估计将近似查询实际返回,更新或删除的行数。
回到我们的例子:
```
EXPLAIN SELECT * FROM tenk1;
QUERY PLAN
-------------------------------------------------------------
Seq Scan on tenk1 (cost=0.00..458.00 rows=10000 width=244)
```
这些数字非常直接地得出。 如果您这样做:
```sql
SELECT relpages, reltuples FROM pg_class WHERE relname = 'tenk1';
```
您会发现tenk1具有358个磁盘页面和10000行。 估计成本的计算方式为:(读取的磁盘页面* seq_page_cost)+(扫描的行* cpu_tuple_cost)。 默认情况下,seq_page_cost为1.0,cpu_tuple_cost为0.01,因此估计成本为(358 * 1.0)+(10000 * 0.01)= 458。
现在,让我们修改查询以添加WHERE条件:
```
EXPLAIN SELECT * FROM tenk1 WHERE unique1 < 7000;
QUERY PLAN
------------------------------------------------------------
Seq Scan on tenk1 (cost=0.00..483.00 rows=7001 width=244)
Filter: (unique1 < 7000)
```
请注意,EXPLAIN输出显示WHERE子句被应用为附加到Seq扫描计划节点的“过滤器”条件。 这意味着计划节点检查其扫描的每一行的条件,并仅输出通过条件的行。 由于使用了WHERE子句,因此减少了对输出行的估计。 但是,扫描仍将必须访问所有10000行,因此成本并未降低。 实际上,它已经增加了一点(确切地说是10000 * cpu_operator_cost),以反映检查WHERE条件所花费的额外CPU时间。
该查询将选择的实际行数为7000,但行估计数仅为近似值。 如果您尝试重复此实验,则可能会得出略有不同的估算值; 而且,它可以在每个ANALYZE命令之后更改,因为ANALYZE生成的统计信息取自该表的随机样本。
现在,让我们对条件进行更严格的限制:
```
EXPLAIN SELECT * FROM tenk1 WHERE unique1 < 100;
QUERY PLAN
------------------------------------------------------------------------------
Bitmap Heap Scan on tenk1 (cost=5.07..229.20 rows=101 width=244)
Recheck Cond: (unique1 < 100)
-> Bitmap Index Scan on tenk1_unique1 (cost=0.00..5.04 rows=101 width=0)
Index Cond: (unique1 < 100)
```
在这里,计划者决定使用两步计划:子计划节点访问索引以查找与索引条件匹配的行的位置,然后上层计划节点实际上从表本身获取这些行。 单独获取行比顺序读取要昂贵得多,但是由于并非必须访问表的所有页面,因此这比顺序扫描便宜。 (使用两个计划级别的原因是,上层计划节点在读取之前将索引标识的行位置按物理顺序排序,以最大程度地减少单独提取的开销。节点名称中提到的“位图”是一种机制, 进行排序。)
现在让我们向WHERE子句添加另一个条件:
```
EXPLAIN SELECT * FROM tenk1 WHERE unique1 < 100 AND stringu1 = 'xxx';
QUERY PLAN
------------------------------------------------------------------------------
Bitmap Heap Scan on tenk1 (cost=5.04..229.43 rows=1 width=244)
Recheck Cond: (unique1 < 100)
Filter: (stringu1 = 'xxx'::name)
-> Bitmap Index Scan on tenk1_unique1 (cost=0.00..5.04 rows=101 width=0)
Index Cond: (unique1 < 100)
```
添加的条件stringu1 ='xxx'减少了输出行数估算值,但不会降低成本,因为我们仍然必须访问相同的行集。 请注意,stringu1子句不能用作索引条件,因为该索引仅在unique1列上。 而是将其作为过滤器应用于索引检索的行。 因此,成本实际上略有上升,以反映此额外的检查。
在某些情况下,计划人员将首选“简单”的索引扫描计划:
```
EXPLAIN SELECT * FROM tenk1 WHERE unique1 = 42;
QUERY PLAN
-----------------------------------------------------------------------------
Index Scan using tenk1_unique1 on tenk1 (cost=0.29..8.30 rows=1 width=244)
Index Cond: (unique1 = 42)
```
##### 14.1.2 EXPLAIN ANALYZE
##### 14.1.3 Caveats
#### 14.2 Statistics Used by the Planner
##### 14.2.1 Single-Column Statistics
##### 14.2.2 Extended Statistics
#### 14.3 Controlling the Planner with Explicit JOIN Clauses
#### 14.4 Populating a Database
首次填充数据库时,可能需要插入大量数据。 本节包含有关如何使此过程尽可能高效的一些建议。
##### 14.4.1 Disable Autocommit
使用多个INSERT时,请关闭自动提交,最后只进行一次提交。 (在普通的SQL中,这意味着在开始时发出BEGIN,在结束时发出COMMIT。某些客户端库可能会在后台执行此操作,在这种情况下,您需要确保在需要时执行该库。)如果允许 每个插入分别提交,PostgreSQL对添加的每一行都做了很多工作。 在一个事务中进行所有插入的另一个好处是,如果插入一行失败,那么将回滚直到该点为止插入的所有行的插入,因此不会卡住部分加载的数据。
##### 14.4.2 Use COPY
使用COPY可以在一个命令中加载所有行,而不是使用一系列INSERT命令。 COPY命令已针对加载大量行进行了优化。 它不如INSERT灵活,但是对于大数据加载而言,开销却大大减少。 由于COPY是单个命令,因此如果使用此方法填充表,则无需禁用自动提交。
如果无法使用COPY,则使用PREPARE创建准备的INSERT语句,然后根据需要使用EXECUTE多次可能会有所帮助。 这避免了重复分析和计划INSERT的一些开销。 不同的接口以不同的方式提供此功能。 在界面文档中查找“准备好的语句”。
请注意,即使使用PREPARE并且将多个插入批处理到单个事务中,使用COPY装载大量行几乎总是比使用INSERT更快。
与早期的CREATE TABLE或TRUNCATE命令在同一事务中使用时,COPY最快。 在这种情况下,无需写入WAL,因为如果发生错误,则无论如何都将删除包含新加载的数据的文件。 但是,此注意事项仅在wal_level最小时适用,因为否则所有命令都必须写入WAL。
##### 14.4.3 Remove Indexes
如果要加载新创建的表,最快的方法是创建表,使用COPY批量加载表的数据,然后创建表所需的任何索引。 在加载每行时,在现有数据上创建索引比以增量方式更新索引更快。
如果要向现有表中添加大量数据,则删除索引,加载表然后重新创建索引可能是一个不错的选择。 当然,在丢失索引期间,其他用户的数据库性能可能会受到影响。 在删除唯一索引之前,还应该三思而后行,因为在缺少索引的情况下,唯一约束所提供的错误检查将丢失。
##### 14.4.4 Remove Foreign Key Constraints
就像索引一样,外键约束的检查比逐行检查的效率更高。 因此,删除外键约束,加载数据并重新创建约束可能很有用。 同样,在缺少约束的情况下,需要在数据加载速度和错误检查丢失之间进行权衡。
此外,当您将数据加载到具有现有外键约束的表中时,每个新行都需要在服务器的未决触发事件列表中添加一个条目(因为触发该行的触发器会检查该行的外键约束)。 加载数百万行可能会导致触发事件队列溢出可用内存,从而导致无法忍受的交换甚至是命令的彻底失败。 因此,在加载大量数据时,可能有必要(不仅希望如此)删除并重新应用外键。 如果暂时删除约束是不可接受的,则唯一的其他方法可能是将装入操作拆分为较小的事务。
##### 14.4.5 Increase maintenance_work_mem
加载大量数据时临时增加maintenance_work_mem配置变量可以提高性能。 这将有助于加快CREATE INDEX命令和ALTER TABLE ADD FOREIGN KEY命令的速度。 它对于COPY本身并没有多大帮助,因此,仅当您使用上述一种或两种技术时,此建议才有用。
在Postgres-XL中,只有分布式列可以具有外键约束。
##### 14.4.6 Increase max_wal_size
临时增加max_wal_size配置变量还可以使大数据加载更快。 这是因为将大量数据加载到PostgreSQL中将导致检查点的发生频率比正常检查点频率(由checkpoint_timeout配置变量指定)高。 每当出现检查点时,都必须将所有脏页刷新到磁盘。 通过在批量数据加载期间临时增加max_wal_size,可以减少所需的检查点数量。
请注意,您应该在所有涉及的节点中调整配置变量。 请记住,数据节点比协调器更重要,并且将需要更多资源。 通过DDL。
##### 14.4.7 Disable WAL Archival and Streaming Replication
将大量数据加载到使用WAL归档或流复制的安装中时,加载完成后进行新的基本备份可能比处理大量增量WAL数据更快。 为了防止在加载时递增的WAL日志记录,请通过将wal_level设置为minimum,将archive_mode设置为off并将max_wal_senders设置为零来禁用归档和流复制。 但是请注意,更改这些设置需要重新启动服务器。
除了避免使存档程序或WAL发送方花费时间来处理WAL数据外,这样做实际上会使某些命令更快,因为如果wal_level最小,则它们根本不会写入WAL。 (与编写WAL相比,最后通过执行fsync可以更廉价地保证崩溃安全。)这适用于以下命令:
- CREATE TABLE AS SELECT
- `CREATE INDEX` (and variants such as `ALTER TABLE ADD PRIMARY KEY`)
- ALTER TABLE SET TABLESPACE
- CLUSTER
- COPY FROM,在同一事务中较早地创建或截断了目标表时
##### 14.4.8 Run ANALYZE Afterwards
只要您显着更改了表中数据的分布,就强烈建议运行ANALYZE。 这包括将大量数据批量加载到表中。 运行ANALYZE(或VACUUM ANALYZE)可确保计划者拥有有关表的最新统计信息。 没有统计信息或统计信息过时,规划器可能会在查询计划期间做出错误的决策,从而导致统计信息不正确或不存在的任何表的性能都较差。 请注意,如果启用了autovacuum守护程序,则它可能会自动运行ANALYZE; 有关更多信息,请参见第24.1.3节和第24.1.6节。
在Postgres-XL中,手动VACUUM也将被下推到所有Datanode。 根据您的工作负载,如果群集负载较高,则可能希望在特定时间禁用autovacuum和vacuum。
##### 14.4.9 Some Notes About pg_dump
pg_dump生成的转储脚本会自动应用上述准则中的一些,但不是全部。 要尽快重新加载pg_dump转储,您需要手动做一些额外的事情。 (请注意,这些要点在还原转储时适用,而不是在创建转储时适用。无论是使用psql加载文本转储还是使用pg_restore从pg_dump存档文件中加载,这些要点均适用。)
默认情况下,pg_dump使用COPY,并且当它生成一个完整的模式和数据转储时,在创建索引和外键之前要小心加载数据。 因此,在这种情况下,会自动处理一些准则。 您要做的是:
- 为maintenance_work_mem和max_wal_size设置适当的值(即大于正常值)。
- 如果使用WAL归档或流复制,请考虑在还原过程中将其禁用。 为此,在加载转储之前,将archive_mode设置为off,将wal_level设置为minimal,将max_wal_senders设置为零。 然后,将它们重新设置为正确的值,然后重新进行基本备份。
- 对pg_dump和pg_restore的并行转储和还原模式进行试验,并找到要使用的最佳并行作业数。 通过-j选项并行转储和还原应该比串行模式具有更高的性能。
- 考虑是否应将整个转储作为单个事务还原。 为此,请将-1或--single-transaction命令行选项传递给psql或pg_restore。 使用此模式时,即使最小的错误也将回滚整个还原,可能会丢弃许多小时的处理时间。 根据数据之间的相互关系,这似乎比手动清理更可取。 如果您使用单个事务并关闭WAL归档,则COPY命令将以最快的速度运行。
- 如果数据库服务器中有多个CPU,请考虑使用pg_restore的--jobs选项。 这允许并发数据加载和索引创建。
- 之后运行ANALYZE。
仅数据转储仍将使用COPY,但不会删除或重新创建索引,并且通常不会触摸外键。 [17]因此,在加载仅数据转储时,如果您希望使用这些技术,则需要删除并重新创建索引和外键。 在加载数据时增加max_wal_size仍然很有用,但是不必费心增加maintenance_work_mem; 相反,您会在之后手动重新创建索引和外键的同时这样做。 完成后,别忘了进行分析; 有关更多信息,请参见第24.1.3节和第24.1.6节。
#### 14.5 Non-Durable Settings
耐用性是一种数据库功能,即使服务器崩溃或断电,也可以保证记录已提交的事务。 但是,持久性会增加大量的数据库开销,因此,如果您的站点不需要这样的保证,则可以将PostgreSQL配置为运行得更快。 以下是在这种情况下可以进行以提高性能的配置更改。 除非另有说明,否则在数据库软件崩溃的情况下仍然可以保证持久性; 使用这些设置时,只有突然的操作系统停止才会造成数据丢失或损坏的风险。
- 将数据库集群的数据目录放置在内存支持的文件系统(即RAM磁盘)中。 这样就消除了所有数据库磁盘的I / O,但将数据存储限制为可用内存量(可能是交换空间)。
- 关闭fsync; 无需将数据刷新到磁盘。
- 关闭sync_commit; 可能无需在每次提交时都强制将WAL写到磁盘。 如果数据库崩溃,此设置会带来事务丢失(但不会破坏数据)的风险。
- 关闭full_page_writes; 没有必要防止部分页面写入。
- 增加max_wal_size和checkpoint_timeout; 这减少了检查点的频率,但是增加了/ pg_wal的存储要求。
- 创建未记录的表以避免WAL写入,尽管这会使表成为非崩溃安全的。
### 15.Parallel Query
PostgreSQL可以设计查询计划,该计划可以利用多个CPU来更快地响应查询。 此功能称为并行查询。 由于当前实现的局限性,或者由于没有可以想象的查询计划快于串行查询计划,许多查询无法从并行查询中受益。 但是,对于可以受益的查询,并行查询的速度通常非常重要。 使用并行查询时,许多查询的运行速度可以提高两倍以上,而某些查询的运行速度可以提高四倍甚至更高。 接触大量数据但仅向用户返回几行的查询通常会受益最多。 本章详细说明了并行查询的工作原理以及在何种情况下可以使用并行查询,以便希望使用并行查询的用户可以理解预期的结果。
#### 15.1 How Parallel Query Works
当优化器确定并行查询是特定查询的最快执行策略时,它将创建一个查询计划,其中包括一个Gather或Gather Merge节点。 这是一个简单的示例:
```
EXPLAIN SELECT * FROM pgbench_accounts WHERE filler LIKE '%x%';
QUERY PLAN
-------------------------------------------------------------------------------------
Gather (cost=1000.00..217018.43 rows=1 width=97)
Workers Planned: 2
-> Parallel Seq Scan on pgbench_accounts (cost=0.00..216018.33 rows=1 width=97)
Filter: (filler ~~ '%x%'::text)
(4 rows)
```
在所有情况下,“聚集”或“聚集合并”节点将只具有一个子计划,这是将并行执行的计划的一部分。 如果“聚集”或“聚集合并”节点位于计划树的最顶部,则整个查询将并行执行。 如果它在计划树中的其他位置,则仅计划下方的部分将并行运行。 在上面的示例中,查询仅访问一个表,因此除Gather节点本身外,仅存在一个计划节点。 由于该计划节点是Gather节点的子节点,因此它将并行运行。
使用EXPLAIN,您可以看到计划者选择的工人数量。在查询执行过程中到达“收集”节点时,正在执行用户会话的进程将请求多个后台工作进程,这些进程等于计划者选择的工作人数。计划者将考虑使用的后台工作者数量最多为max_parallel_workers_per_gather。一次可以存在的后台工作程序总数受max_worker_processes和max_parallel_workers限制。因此,并行查询有可能以少于计划的工作人员运行,甚至根本没有工作。最佳计划可能取决于可用工作程序的数量,因此这可能会导致查询性能下降。如果这种情况经常发生,请考虑增加max_worker_processes和max_parallel_workers,以便可以同时运行更多工作程序,或者减少max_parallel_workers_per_gather,以便计划者减少工作程序的数量。
对于给定的并行查询成功启动的每个后台工作进程将执行计划的并行部分。 领导者还将执行计划的那部分,但是它还有另外的责任:它还必须读取工人产生的所有元组。 当计划的并行部分仅生成少量元组时,领导者的行为通常会非常类似于额外的工作程序,从而加快了查询的执行速度。 相反,当计划的并行部分生成大量的元组时,领导者可能几乎完全被读取工人所生成的元组并执行计划节点在“聚集”节点级别之上所需的任何其他处理步骤所占据 或“收集合并”节点。 在这种情况下,发起者将很少执行计划的并行部分的工作。
当计划并行部分顶部的节点是Gather Merge而不是Gather时,表明执行计划并行部分的每个进程都按排序顺序生成元组,并且发起者正在执行顺序保留合并 。 相反,Gather以任何方便的顺序从工人读取元组,从而破坏了可能存在的任何排序顺序。
#### 15.2 When Can Parallel Query Be Used?
#### 15.3 Parallel Plans
##### 15.3.1 Parallel Scans
##### 15.3.2 Parallel Joins
##### 15.3.3 Parallel Aggregation
##### 15.3.4 Parallel Plan Tips
#### 15.4 Parallel Safety
##### 15.4.1 Parallel Labeling for Functions and Aggregation
## Server Administration
本部分介绍PostgreSQL数据库管理员感兴趣的主题。主要包含如何安装、配置服务器、管理用户和数据库以及任务维护.运行PostgreSQL服务器(甚至供个人使用,尤其是在生产中使用)的任何人都应该熟悉本部分介绍的主题。
此部分中的信息大约按照新用户阅读该信息的顺序排列。 但是这些章节是独立的,可以根据需要单独阅读。 本部分中的信息以主题单元的叙述方式呈现。 寻求特定命令完整描述的读者应参阅第六部分。
撰写了最初的几章,因此无需先决知识即可理解它们,因此需要设置自己的服务器的新用户可以从这一部分开始探索。 本部分的其余部分与调优和管理有关。 该材料假定读者熟悉PostgreSQL数据库系统的一般用法。 鼓励读者阅读第一部分和第二部分以获取更多信息。
### Installation from Source Code
本章将介绍如何使用源码进行PostgreSQL的安装(如果要安装预打包的发行版,例如RPM或Debian软件包,请忽略本章,而阅读打包程序的说明。)
#### Short Version
接下来一个简单的安装允许我们再本地安装一个包含1个协调节点、2个数据节点、1个GTM节点的简单的集群.当我们安装一个复杂的集群时,我们可能会调整协调节点、数据节点的数量并且有可能在不同的server上启动节点.当然,我们可以使用`pgxc_ctl`工具,这个工具可以简化我们的安装和配置操作.
```shell
#编译
./configure
gmake
su
gmake install
#添加用户创建目录
adduser postgres
mkdir /usr/local/pgsql/data_coord1
mkdir /usr/local/pgsql/data_datanode_1
mkdir /usr/local/pgsql/data_datanode_2
mkdir /usr/local/pgsql/data_gtm
chown postgres /usr/local/pgsql/data_coord1
chown postgres /usr/local/pgsql/data_datanode_1
chown postgres /usr/local/pgsql/data_datanode_2
chown postgres /usr/local/pgsql/data_gtm
#切换用户及初始化数据目录
su - postgres
/usr/local/pgsql/bin/initdb -D /usr/local/pgsql/data_coord1 \
--nodename coord1
/usr/local/pgsql/bin/initdb -D /usr/local/pgsql/data_datanode_1 \
--nodename datanode_1
/usr/local/pgsql/bin/initdb -D /usr/local/pgsql/data_datanode_2 \
--nodename datanode_2
/usr/local/pgsql/bin/initgtm -D /usr/local/pgsql/data_gtm -Z gtm
/usr/local/pgsql/bin/gtm -D /usr/local/pgsql/data_gtm >logfile 2>&1 &
#启动节点信息
/usr/local/pgsql/bin/postgres --datanode -p 15432 -c pooler_port=40101 \
-D /usr/local/pgsql/data_datanode_1 >logfile 2>&1 &
/usr/local/pgsql/bin/postgres --datanode -p 15433 -c pooler_port=40102 \
-D /usr/local/pgsql/data_datanode_2 >logfile 2>&1 &
/usr/local/pgsql/bin/postgres --coordinator -c pooler_port=40100 \
-D /usr/local/pgsql/data_coord1 >logfile 2>&1 &
/usr/local/pgsql/bin/psql -c "ALTER NODE coord1 \
WITH (TYPE = 'coordinator', PORT = 5432)" postgres
/usr/local/pgsql/bin/psql -c "CREATE NODE datanode_1 \
WITH (TYPE = 'datanode', PORT = 15432)" postgres
/usr/local/pgsql/bin/psql -c "CREATE NODE datanode_2 \
WITH (TYPE = 'datanode', PORT = 15433)" postgres
/usr/local/pgsql/bin/psql -c "EXECUTE DIRECT ON (datanode_1) \
'ALTER NODE datanode_1 WITH (TYPE = ''datanode'', PORT = 15432)'" postgres
/usr/local/pgsql/bin/psql -c "EXECUTE DIRECT ON (datanode_1) \
'CREATE NODE datanode_2 WITH (TYPE = ''datanode'', PORT = 15433)'" postgres
/usr/local/pgsql/bin/psql -c "EXECUTE DIRECT ON (datanode_1) \
'CREATE NODE coord1 WITH (TYPE = ''coordinator'', PORT = 5432)'" postgres
/usr/local/pgsql/bin/psql -c "EXECUTE DIRECT ON (datanode_2) \
'ALTER NODE datanode_2 WITH (TYPE = ''datanode'', PORT = 15433)'" postgres
/usr/local/pgsql/bin/psql -c "EXECUTE DIRECT ON (datanode_2) \
'CREATE NODE datanode_1 WITH (TYPE = ''datanode'', PORT = 15432)'" postgres
/usr/local/pgsql/bin/psql -c "EXECUTE DIRECT ON (datanode_2) \
'CREATE NODE coord1 WITH (TYPE = ''coordinator'', PORT = 5432)'" postgres
/usr/local/pgsql/bin/psql -c "SELECT pgxc_pool_reload()" postgres
/usr/local/pgsql/bin/psql -c "EXECUTE DIRECT ON (datanode_1) \
'SELECT pgxc_pool_reload()'" postgres
/usr/local/pgsql/bin/psql -c "EXECUTE DIRECT ON (datanode_2) \
'SELECT pgxc_pool_reload()'" postgres
/usr/local/pgsql/bin/createdb test
/usr/local/pgsql/bin/psql test
```
接下来的章节将进行详细的说明.
#### Requirements
通常,现代的Unix兼容平台应能够运行PostgreSQL.下文第16.6节列出了在发布时已接受特定测试的平台。在发行版的doc子目录中,如果遇到问题,您可能希望查阅几个特定于平台的FAQ文档。
要构建PostgreSQL,需要以下软件包:
- GNU make 版本要3.80+ ,其他的make程序或者老版本的GNU make 是不被支持的(GNU make有时也被安装叫做gmake),测试GNU make 输入如下命令:
make --version
- 您需要一个ISO / ANSI C编译器(至少与C89兼容)。 建议使用最新版本的GCC,但已知PostgreSQL使用来自不同供应商的多种编译器来构建。
- 除了gzip或bzip2之外,还需要tar来解压缩源代码发行包。
- 默认情况下使用GNU Readline库。 它允许psql(PostgreSQL命令行SQL解释器)记住您键入的每个命令,并允许您使用箭头键来调用和编辑先前的命令。 这非常有帮助,强烈建议您这样做。 如果您不想使用它,则必须指定--without-readline选项进行配置。 作为替代方案,您通常可以使用最初在NetBSD上开发的BSD许可的libedit库。 libedit库是GNU Readline兼容的,如果找不到libreadline,或者将--with-libedit-preferred用作配置选项,则使用该库。 如果您使用的是基于软件包的Linux发行版,请注意,如果发行版中的软件包是独立的,则需要readline和readline-devel软件包。
- 默认情况下使用zlib压缩库。 如果您不想使用它,则必须指定--without-zlib选项进行配置。 使用此选项将禁用对pg_dump和pg_restore中的压缩归档的支持。
接下来的安装包是可选的.他们在默认配置中不是必须的.但是当相关构建的配置项被开启是,他们则是必须的,具体如下:
- 要构建服务器编程语言PL / Perl,您需要完整的Perl安装,包括libperl库和头文件。 最低要求的版本是Perl 5.8.3。 由于PL / Perl将是共享库,因此在大多数平台上,libperl库也必须是共享库。 在最新的Perl版本中,这似乎是默认设置,但在较早的版本中则不是默认设置,无论如何,这是您在站点上安装Perl的人的选择。 如果选择了构建PL / Perl,但是configure找不到共享的libperl,则configure将会失败。 在这种情况下,您将必须手动重建并安装Perl才能构建PL / Perl。 在Perl的配置过程中,请求共享库。
如果您打算不仅仅使用PL / Perl,还应确保在启用usemultiplicity选项的情况下构建Perl安装(perl -V将显示是否是这种情况)。
- 要构建PL / Python服务器编程语言,您需要安装Python,其中包含头文件和distutils模块。 最低要求的版本是Python 2.4。 如果版本3或更高版本,则支持Python 3。 但使用Python 3时请参阅第45.1节。
由于PL / Python将是共享库,因此在大多数平台上,libpython库也必须是共享库。 从源代码构建的默认Python安装中不是这种情况,但是在许多操作系统发行版中都可以使用共享库。 如果选择了构建PL / Python,但configure会失败,但找不到共享的libpython。 这可能意味着您必须安装其他软件包或重建Python安装(的一部分)才能提供此共享库。 从源代码构建时,请使用--enable-shared标志运行Python的configure。
- 要构建PL / Tcl过程语言,您当然需要安装Tcl。 最低要求的版本是Tcl 8.4。
- 要启用本地语言支持(NLS),即能够以英语以外的其他语言显示程序消息的功能,您需要实现Gettext API。 某些操作系统具有此内置功能(例如Linux,NetBSD,Solaris),对于其他系统,您可以从http://www.gnu.org/software/gettext/下载附加软件包。 如果您在GNU C库中使用Gettext实现,那么对于某些实用程序,您将另外需要GNU Gettext包。 对于任何其他实现,您将不需要它。
- 如果要支持加密的客户端连接,则需要OpenSSL。 最低要求的版本是0.9.8。
- 如果要使用这些服务支持身份验证,则需要Kerberos,OpenLDAP和/或PAM。
- 要构建PostgreSQL文档,有一套单独的要求。
如果要从Git树构建而不是使用已发布的源软件包,或者要进行服务器开发,则还需要以下软件包:
- 从Git检出,或者如果您更改了实际的扫描程序和解析器定义文件,则需要GNU Flex和Bison。 如果需要它们,请确保获得Flex 2.5.31或更高版本以及Bison 1.875或更高版本。 不能使用其他lex和yacc程序。
- 从Git检出,或者如果您更改了使用Perl脚本的任何构建步骤的输入文件,都需要Perl 5.8.3或更高版本。 如果在Windows上构建,则无论如何都需要Perl。 还需要Perl运行一些测试套件。
如果需要获取GNU软件包,则可以在本地GNU镜像站点(请参阅http://www.gnu.org/order/ftp.html以获得列表)或ftp://ftp.gnu.org/gnu/
还要检查您是否有足够的磁盘空间。 编译期间,您将需要约100 MB的源树,而安装目录将需要约20 MB。 一个空的数据库集群大约需要35 MB。 数据库占用的空间大约是具有相同数据的纯文本文件所需要的空间的五倍。 如果要运行回归测试,则暂时需要额外的150 MB。 使用df命令检查可用磁盘空间。
#### Getting The Source
你可以直接从版本仓库中获取源码,具体查看Appendix I
#### Installation Procedure
**Configuration**
安装程序的第一步就是基于你的系统及你选择的配置项来对源码进行编译前的配置.当然,这个操作我们是通过`configure`脚本来进行配置.对于默认的配置如下:
```shell
./configure
```
该脚本将运行许多测试,以确定各种系统相关变量的值并检测操作系统的任何问题,最后将在构建树中创建几个文件来记录发现的内容。 如果要将构建目录分开,还可以在源树之外的目录中运行configure。 此过程也称为VPATH构建。 这是如何做
```shell
mkdir build_dir
cd build_dir
/path/to/source/tree/configure [options go here]
make
```
默认配置将构建服务器和实用程序,以及仅需要C编译器的所有客户端应用程序和接口。 默认情况下,所有文件都将安装在/ usr / local / pgsql下
当然你也可以自定义相关参数进行程序的配置,我们只想在`configure` 输入如下参数可选项:
--prefix=PREFIX
安装所有文件在指定的PREFIX目录下,而不是默认的/usr/local/pgsql.实际文件将被安装到各个子目录中。 没有文件将直接安装到PREFIX目录中。
如果您有特殊需要,还可以使用以下选项来自定义各个子目录。 但是,如果将它们保留为默认值,则安装将可重定位,这意味着您可以在安装后移动目录。 (man和doc位置不受此影响。)
对于可重定位的安装,您可能要使用configure的--disable-rpath选项。 另外,您将需要告诉操作系统如何找到共享库。
--exec-prefix=EXEC-PREFIX
您可以在与PREFIX设置不同的前缀EXEC-PREFIX下安装与体系结构相关的文件。 这对于在主机之间共享与体系结构无关的文件很有用。 如果您省略此设置,则EXEC-PREFIX设置为等于PREFIX,并且与体系结构相关的文件和与文件无关的文件都将安装在同一棵树下,这可能正是您想要的。
--bindir=DIRECTORY
指明可执行程序的目录.默认是在EXEC-PREFIX/bin,通常来说实在/usr/local/pgsql/bin目录下
--sysconfdir=DIRECTORY
设置各个配置文件存放的目录,默认是在PREFIX/etc
--libdir=DIRECTORY
设置安装库和可动态加载模块的位置。 默认值为EXEC-PREFIX / lib。
--includedir=DIRECTORY
设置用于安装C和C ++头文件的目录。 默认值为PREFIX / include。
--datarootdir=DIRECTORY
为各种类型的只读数据文件设置根目录。 这仅为以下某些选项设置默认值。 默认值为PREFIX / share。
--datadir=DIRECTORY
设置已安装程序使用的只读数据文件的目录。 默认值为DATAROOTDIR。 请注意,这与数据库文件的放置位置无关。
--localedir=DIRECTORY
设置用于安装区域设置数据的目录,尤其是消息翻译目录文件。 默认值为DATAROOTDIR / locale。
--mandir=DIRECTORY
PostgreSQL随附的手册页将安装在该目录下的相应manx子目录下。 默认值为DATAROOTDIR / man。
--docdir=DIRECTORY
设置用于安装文档文件的根目录,“ man”页面除外。 这仅为以下选项设置默认值。 此选项的默认值为DATAROOTDIR / doc / postgresql。
--htmldir=DIRECTORY
PostgreSQL的HTML格式文档将安装在此目录下。 默认值为DATAROOTDIR。
--with-extra-version=STRING
将STRING附加到PostgreSQL版本号。 例如,您可以使用它来标记由未发布的Git快照构建的二进制文件,或包含带有额外版本字符串(例如git describe标识符或分发软件包发行版号)的自定义补丁的二进制文件。
--with-includes=DIRECTORY
DIRECTORIES是用冒号分隔的目录列表,这些目录将添加到编译器搜索头文件的列表中。 如果在非标准位置安装了可选软件包(例如GNU Readline),则必须使用此选项,并且可能还要使用相应的--with-libraries选项。
--with-libraies=DIRECTORY
DIRECTORIES是用冒号分隔的目录列表,用于搜索库。 如果您将软件包安装在非标准位置,则可能必须使用此选项(以及相应的--with-includes选项)。
--enable-nls[=LANGUAGES]
启用本机语言支持(NLS),即以英语以外的其他语言显示程序消息的功能。 LANGUAGES是一个可选的用空格分隔的列表,列出要支持的语言,例如--enable-nls ='de fr'。 (将自动计算列表与实际提供的翻译集之间的交集。)如果不指定列表,则将安装所有可用的翻译。
要使用此选项,您将需要实现Gettext API。 往上看。
--with-pgport=NUMBER
将NUMBER设置为服务器和客户端的默认端口号。 默认值为5432。以后始终可以更改端口,但是如果在此处指定端口,则服务器和客户端都将具有相同的默认编译值,这非常方便。 通常,选择非默认值的唯一很好的理由是,如果您打算在同一台计算机上运行多个PostgreSQL服务器。
--with-perl
构建PL / Perl服务器端语言。
--with-python
构建PL / Python服务器端语言。
--with-tcl
构建PL / Tcl服务器端语言。
--with-tclconfig
Tcl安装文件tclConfig.sh,其中包含构建与Tcl接口的模块所需的配置信息。 通常会在一个众所周知的位置自动找到此文件,但是如果您要使用其他版本的Tcl,则可以指定要在其中查找文件的目录
--with-gssapi
建立对GSSAPI身份验证的支持。 在许多系统上,GSSAPI(通常是Kerberos安装的一部分)系统未安装在默认搜索的位置(例如/ usr / include,/ usr / lib),因此必须使用--with选项 -includes和--with-libraries以及该选项。 configure会在继续之前检查所需的头文件和库,以确保您的GSSAPI安装足够。
--with-krb-srvnam
GSSAPI使用的Kerberos服务主体的默认名称。 postgres是默认值。 除非您具有Windows环境,否则通常没有理由更改此设置,在这种情况下,必须将其设置为大写POSTGRES。
--with-icu
建立对ICU库的支持。 这需要安装ICU4C软件包。 当前,ICU4C的最低要求版本为4.2。
默认情况下,将使用pkg-config查找所需的编译选项。 ICU4C 4.6版和更高版本支持此功能。 对于较旧的版本,或者如果pkg-config不可用,则可以指定变量ICU_CFLAGS和ICU_LIBS进行配置,例如以下示例:
./configure ... --with-icu ICU_CFLAGS ='-I / some / where / include'ICU_LIBS ='-L / some / where / lib -licui18n -licuuc -licudata'
(如果ICU4C在编译器的默认搜索路径中,那么您仍需要指定一个非空字符串,以避免使用pkg-config,例如ICU_CFLAGS =''。)
--with-openssl
建立对SSL(加密)连接的支持。 这需要安装OpenSSL软件包。 configure将在继续之前检查所需的头文件和库,以确保您的OpenSSL安装足够。
--with-pam
使用PAM(可插入身份验证模块)支持进行构建。
--with-bsd-auth
带有BSD身份验证支持。 (BSD身份验证框架当前仅在OpenBSD上可用。)
--with-ldap
使用LDAP支持进行构建以进行身份验证和连接参数查找(有关更多信息,请参见第33.17节和第20.3.7节)。 在Unix上,这需要安装OpenLDAP软件包。 在Windows上,使用默认的WinLDAP库。 configure将在继续之前检查所需的头文件和库,以确保您的OpenLDAP安装足够。
--with-systemd
建立对systemd服务通知的支持。 如果服务器二进制文件是在systemd下启动的,则可以提高集成度,否则不会产生任何影响。 有关更多信息,请参见第18.3节。 需要安装libsystemd及其关联的头文件,才能使用此选项。
--without-readline
禁止使用Readline库(以及libedit)。 此选项禁用psql中的命令行编辑和历史记录,因此不建议使用。
--with-libedit-preferred
赞成使用BSD许可的libedit库,而不是GPL许可的Readline。 仅当您同时安装了两个库时,此选项才有意义。 在这种情况下,默认设置是使用Readline。
--with-bonjour
在Bonjour支持下进行构建。 这需要您的操作系统中的Bonjour支持。 在macOS上推荐。
--with-uuid
使用指定的UUID库构建uuid-ossp模块(该模块提供生成UUID的功能)。 库必须是以下之一:
- bsd使用在FreeBSD,NetBSD和其他BSD派生的系统中找到的UUID函数
- e2fs使用e2fsprogs项目创建的UUID库; 该库存在于大多数Linux系统和macOS中,并且也可以在其他平台上获得
- ossp使用OSSP UUID库
--with-ossp-uuid
with-uuid = ossp的过时等效项。
--with-libxml
使用libxml构建(启用SQL / XML支持)。 此功能需要Libxml 2.6.23版或更高版本。
Libxml安装程序xml2-config,该程序可用于检测所需的编译器和链接器选项。 如果发现,PostgreSQL将自动使用它。 要在不寻常的位置指定libxml安装,可以将环境变量XML2_CONFIG设置为指向属于该安装的xml2-config程序,也可以使用--with-includes和--with-libraries选项。
--with-libxslt
构建xml2模块时,请使用libxslt。 xml2依靠此库执行XML的XSL转换。
--disable-float4-byval
禁用“按值”传递float4值,从而使其改为“按引用”传递。 此选项会降低性能,但可能需要与使用C编写并使用“版本0”调用约定的旧用户定义函数兼容。 更好的长期解决方案是将所有此类功能更新为使用“版本1”调用约定。
--disable-float8-byval
禁用“按值”传递float8值,从而使其改为“按引用”传递。 此选项会降低性能,但可能需要与使用C编写并使用“版本0”调用约定的旧用户定义函数兼容。 更好的长期解决方案是将所有此类功能更新为使用“版本1”调用约定。 请注意,此选项不仅影响float8,还影响int8和一些相关类型,例如时间戳。 在32位平台上,--disable-float8-byval是默认设置,并且不允许选择--enable-float8-byval。
--with-segsize=SEGSIZE
设置段大小(以GB为单位)。 大表分为多个操作系统文件,每个文件的大小等于段的大小。 这避免了许多平台上存在的文件大小限制问题。 默认段大小为1 GB,在所有受支持的平台上都是安全的。 如果您的操作系统支持“大文件”(如今大多数情况下这样做),则可以使用更大的段大小。 这对于减少使用非常大的表时所消耗的文件描述符的数量可能会有所帮助。 但是请注意不要选择大于平台和打算使用的文件系统支持的值。 您可能希望使用的其他工具(例如tar)也可以设置可用文件大小的限制。 建议(尽管不是绝对必需的)此值应为2的幂。请注意,更改此值需要initdb。
--with-blocksize
设置块大小(以千字节为单位)。 这是表中存储和I / O的单位。 默认值8 KB适用于大多数情况。 但是其他值在特殊情况下可能会有用。 该值必须是1到32(千字节)之间的2的幂。 请注意,更改此值需要一个initdb。
--with-wal-segsize
设置WAL段的大小(以兆字节为单位)。 这是WAL日志中每个文件的大小。 调整此大小以控制WAL日志传送的粒度可能很有用。 默认大小为16 MB。 该值必须是1到1024(兆字节)之间的2的幂。 请注意,更改此值需要一个initdb。
--with-wal-blocksize
设置WAL块大小(以千字节为单位)。 这是WAL日志中存储和I / O的单位。 默认值8 KB适用于大多数情况。 但是其他值在特殊情况下可能会有用。 该值必须是1到64(千字节)之间的2的幂。 请注意,更改此值需要一个initdb。
--disable-spinlocks
即使PostgreSQL不支持平台的CPU自旋锁,也要允许构建成功。 缺少自旋锁支持将导致性能不佳; 因此,仅当构建中止并通知您平台缺乏自旋锁支持时,才应使用此选项。 如果在您的平台上构建PostgreSQL需要此选项,请将该问题报告给PostgreSQL开发人员。
--disable-strong-random
即使PostgreSQL在平台上不支持强随机数,也要允许构建成功。 一些身份验证协议以及pgcrypto模块中的某些例程都需要一个随机数源。 --disable-strong-random禁用需要密码强的随机数的功能,并使用弱的伪随机数生成器代替认证盐值和查询取消键的生成。 这可能会使身份验证的安全性降低。
--disable-thread-safety
禁用客户端库的线程安全。 这样可以防止libpq和ECPG程序中的并发线程安全地控制其专用连接句柄。
--with-system-tzdata
PostgreSQL包括自己的时区数据库,它需要日期和时间操作。实际上,该时区数据库与许多操作系统(如FreeBSD,Linux和Solaris)提供的IANA时区数据库兼容,因此再次安装将是多余的。使用此选项时,将使用DIRECTORY中系统提供的时区数据库,而不是PostgreSQL源代码分发中包含的数据库。必须将DIRECTORY指定为绝对路径。 / usr / share / zoneinfo在某些操作系统上可能是目录。请注意,安装例程将不会检测到不匹配或错误的时区数据。如果使用此选项,建议您运行回归测试以验证您所指向的时区数据可在PostgreSQL上正常使用。
此选项主要针对非常了解其目标操作系统的二进制软件包发行商。使用此选项的主要优点是,只要更改许多本地夏时制规则中的任何规则,就无需升级PostgreSQL软件包。另一个优点是,如果在安装过程中不需要构建时区数据库文件,则可以更直接地交叉编译PostgreSQL。
--without-zlib
禁止使用Zlib库。 这将禁用对pg_dump和pg_restore中压缩档案的支持。 此选项仅适用于无法使用此库的罕见系统。
--enable-debug
用调试符号编译所有程序和库。 这意味着您可以在调试器中运行程序以分析问题。 这会大大增加已安装的可执行文件的大小,并且在非GCC编译器上,它通常还会禁用编译器优化,从而导致速度降低。 但是,提供可用的符号对于解决可能出现的任何问题非常有帮助。 当前,仅当您使用GCC时,才建议将此选项用于生产安装。 但是,如果您正在从事开发工作或正在运行Beta版本,则应始终启用它。
--enable-converage
运行时,它们在构建目录中生成带有代码覆盖率指标的文件。 有关更多信息,请参见第32.5节。 此选项仅适用于GCC以及进行开发工作时。
--enable-profiling
如果使用GCC,则将编译所有程序和库,以便对其进行概要分析。 在后端出口,将创建一个子目录,其中包含用于分析的gmon.out文件。 此选项仅适用于GCC以及进行开发工作时。
--enable-cassert
在服务器中启用断言检查,该检查检查许多“不可能发生”的情况。 对于代码开发而言,这是非常宝贵的,但是测试会大大降低服务器的速度。 另外,打开测试并不一定会增强服务器的稳定性! 断言检查没有按照严重性进行分类,因此,如果触发断言失败,则可能是相对无害的错误仍会导致服务器重新启动。 不建议将此选项用于生产环境,但在进行开发工作或运行Beta版本时,应启用此选项。
--enable-depend
启用自动依赖项跟踪。 使用此选项,将设置makefile,以便在更改任何头文件时将重建所有受影响的目标文件。 如果您要进行开发工作,这将很有用,但是如果您只打算编译一次并安装,那么这只会浪费您的开销。 目前,此选项仅适用于GCC。
--enable-dtrace
在支持动态跟踪工具DTrace的情况下编译PostgreSQL。 有关更多信息,请参见第28.5节。
要指向dtrace程序,可以设置环境变量DTRACE。 这通常是必要的,因为dtrace通常安装在/ usr / sbin下,该路径可能不在路径中。
可以在环境变量DTRACEFLAGS中指定dtrace程序的其他命令行选项。 在Solaris上,要在64位二进制文件中包括DTrace支持,必须指定DTRACEFLAGS =“-64”进行配置。 例如,使用GCC编译器:
./configure CC ='gcc -m64'--enable-dtrace DTRACEFLAGS ='-64'...
使用Sun的编译器:
./configure CC ='/ opt / SUNWspro / bin / cc -xtarget = native64'--enable-dtrace DTRACEFLAGS ='-64'...
--enable-tap-tests
使用Perl TAP工具启用测试。 这需要安装Perl和Perl模块IPC :: Run。 有关更多信息,请参见第32.4节。
如果您希望使用与配置选择不同的C编译器,则可以将环境变量CC设置为您选择的程序。 默认情况下,configure将选择gcc(如果可用),否则选择平台的默认值(通常为cc)。 同样,如果需要,可以使用CFLAGS变量覆盖默认的编译器标志。
您可以在configure命令行上指定环境变量,例如:
```shell
./configure CC=/opt/bin/gcc CFLAGS='-O2 -pipe'
```
这是可以以这种方式设置的重要变量的列表:
BISON: Bison 程序
CC: C编译器
CFLAGS:C编译器相关参数配置
CPP:C预处理器
CPPFLAGS:C预处理起相关参数配置
DTRACE:`dtrace`程序路径配置
FLEX: FLEX程序
LDFLAGS:链接可执行文件或共享库时使用的选项
LDFLAGS_EX:仅用于链接可执行文件的其他选项
LDFLAGS_SL:仅用于链接共享库的其他选项
MSGFMT:对于native language支持的msgfmt(消息格式化)程序
PERL:Perl解释器的完整路径名。 这将用于确定构建PL / Perl的依赖性。
PYTHON:Python解释器的完整路径名。 这将用于确定构建PL / Python的依赖关系。 同样,是否在此处指定了Python 2或3(或以其他方式隐式选择)确定了PL / Python语言的哪个变体可用。 有关更多信息,请参见第45.1节。
TCLSH:Tcl解释器的全路径名。 这将用于确定构建PL / Tcl的依赖关系,并将其替换为Tcl脚本。
XML2_CONFG:xml2-config程序,用于定位libxml安装。
有时,事后将编译器标志添加到由configure选择的集合中很有用。 一个重要的示例是gcc的-Werror选项不能包含在传递给配置的CFLAGS中,因为它会破坏许多configure的内置测试。 要添加此类标志,请在运行make时将其包括在COPT环境变量中。 将COPT的内容添加到由configure设置的CFLAGS和LDFLAGS选项中。 例如,您可以
```shell
make COPT='-Werror'
```
或者设置为环境变量
```shell
export COPT='-Werror'
make
```
**Build**
开始编译,输入
```shell
make
```
(记住:使用GNU make工具)构建过程消耗的时间取决于你的硬件.构建完后,最后输出的应该是:
```
All of PostgreSQL successfully made. Ready to install.
```
如果你想构建所有的东西,包括文档(HTML,man 提示)及额外的模块,可以使用
```shell
make world
```
编译最后展示的应该如下:
```
PostgreSQL, contrib, and documentation successfully made. Ready to install.
```
**Regression Tests**
如果你想在安装前测试线你构建的server,你可以进行回归tests。回归测试是一个测试套件,用于验证PostgreSQL是否以开发人员期望的方式在您的计算机上运行。 输入:
```shell
make check
```
(这不能以root用户的身份进行;要以非特权用户的身份进行操作。)第32章包含有关解释测试结果的详细信息。 您可以在以后的任何时间通过发出相同的命令来重复此测试。
**Installing Th Files**
在我们学习如何安装Postgres-XL前,你应该知道你将要在每个server安装些什么.接下来的清单介绍了你将要安装的Postgres-XL组件
GTM
GTM 又叫全局事务管理器,它在整个Postgres-xl 数据库集群中提供全局事务ID及快照信息给每个事务请求.当然他也提供一些其他的全局值,例如序列和全局timestamps
GTM本身可以配置为另一个GTM的备份,作为GTM-Standby,以便即使主GTM发生故障,GTM仍可以继续运行。 您可能需要将GTM-Standby安装到单独的服务器上。
GTM-Proxy
因为GTM必须处理每个事务,所以它必须读取和写入大量消息,这可能会限制Postgres-XL的可伸缩性。 GTM代理是GTM功能的代理,该功能将请求和响应分组以减少GTM对网络的读写。 将一个快照分发给多个事务还有助于减少GTM网络工作量。
Coordinator
协调节点是Postgres-XL 与应用的交互的入口。你可以在集群中运行多个协调节点。每个协调器的行为都像PostgreSQL数据库服务器一样,而所有协调器都以统一的方式处理事务,因此保护进入一个协调器的任何事务免受任何其他进入另一个协调器的事务的影响。 事务更新对在其他协调器中运行的其他人立即可见。 如下所述,为了简化Coordinator和Datanode的负载平衡,强烈建议在服务器中安装相同数量的Coordinator和Datanode。
Datanode
协调节点与数据节点都是共享相同的二进制包但是他们的行为上有一点不同.协调节点统一管理传入的请求,然后把分发给数据节点进行处理.如果有必要,协调节点可实现来至数据节点的响应内容,然后进行计算最终响应给应用程序. 数据节点则非常的像原本的PostgreSQL,因为它仅仅只是处理本地的输入请求.
注意:
如果要升级现有系统,请务必阅读第18.6节,其中包含有关升级群集的说明。
安装PostgreSQL输入如下命令:
```shell
make install
```
执行该命令将安装文件到我们第一步配置的目录中.需要注意的是你有对该目录写入的权限.通常我们应该使用root用户来执行这步操作.另外,您可以提前创建目标目录并安排要授予的适当权限。
要安装文档(HTML和手册页),请输入:
```shell
make install-docs
```
如果你想构建所有,输入:
```shell
make install-world
```
这个操作会安装文档的.
您可以使用make install-strip代替make install在安装可执行文件和库时剥离它们。 这样可以节省一些空间。 如果您是使用调试支持构建的,则剥离将有效地删除调试支持,因此仅在不再需要调试时才应这样做。 install-strip试图节省空间,但是它并不具有如何从可执行文件中剥离每个不需要的字节的完整知识,因此,如果您想节省所有的磁盘空间,则必须这样做 体力劳动。
标准安装提供了客户端应用程序开发以及服务器端程序开发所需的所有头文件,例如用C编写的自定义函数或数据类型。(在PostgreSQL 8.0之前,单独的make install-all-headers命令是 后者需要,但此步骤已经打包到标准安装中。)
**Client-only installation**:如果你只想安装客户端应用与相关依赖包,接下来你可以使用如下这些命令:
```shell
make -C src/bin install
make -C src/include install
make -C src/interfaces install
make -C doc install
```
src/bin 有一些仅用于服务器的二进制文件,但是它们很小。
**Uninstallation**:要撤消安装,请使用命令make uninstall。 但是,这不会删除任何创建的目录
**Cleaning:**安装后,可以通过使用make clean命令从源树中删除构建的文件来释放磁盘空间。 这将保留配置程序生成的文件,以便稍后可以使用make重建所有内容。 要将源树重置为分发状态,请使用make distclean。 如果要在同一源代码树中为多个平台进行构建,则必须执行此操作并为每个平台重新配置。 (或者,为每个平台使用单独的构建树,以使源树保持不变。)
如果执行构建后发现配置选项错误,或者如果更改了配置调查(例如软件升级),那么最好在重新配置和重建之前进行distclean。 否则,您对配置选择的更改可能不会传播到所需的任何地方。
#### Post-Installation Setup
##### Shared Libraries
在某些系统上对于shared libraries 你需要明确的指明怎么来进行查找安装的新shared liberaries。在如下系统是不需要指定的: FressBSD,HP-UX ,Linux,NetBSD,OpenBSD以及Solaris
设置共享库搜索路径的方法因平台而异,但是最广泛使用的方法是设置环境变量LD_LIBRARY_PATH,如下所示:在Bourne shell(sh,ksh,bash,zsh)中:
```shell
LD_LIBRARY_PATH=/usr/local/pgsql/lib
export LD_LIBRARY_PATH
```
或者再csh、tcsh中:
```shell
setenv LD_LIBRARY_PATH /usr/local/pgsql/lib
```
将/ usr / local / pgsql / lib替换为您在步骤1中将--libdir设置为的内容。您应该将这些命令放入Shell启动文件中,例如/ etc / profile或〜/ .bash_profile。 有关与此方法相关的警告的一些良好信息,可以在http://xahlee.org/UnixResource_dir/_/ldpath.html中找到。
在某些系统上,最好在构建之前设置环境变量LD_RUN_PATH。
在Cygwin上,将库目录放在PATH中,或将.dll文件移到bin目录中。
如有疑问,请参阅系统的手册页(可能是ld.so或rld)。 如果以后收到类似以下的消息:
```
psql: error in loading shared libraries
libpq.so.2.1: cannot open shared object file: No such file or directory
```
那么这一步是必要的。 注意你的配置。
如果你使用的是Linux并且你可以使用root管理员,你可以这样配置:
```shell
/sbin/ldconfig /usr/local/pgsql/lib
```
(或等效目录)安装后,以使运行时链接程序更快地找到共享库。 有关更多信息,请参考ldconfig的手册页。 在FreeBSD,NetBSD和OpenBSD上,命令为:
```
/sbin/ldconfig -m /usr/local/pgsql/lib
```
##### Environment Variables
如果您安装到/ usr / local / pgsql或默认情况下不搜索程序的其他位置,你应该将/usr/local/pgsql/bin(或者说你编译时候配置的--bindir目录)配置到环境变量PATH中.严格的来讲,这不是必须的,但是这会方便我们对PostgreSQL的使用
为了提供这个操作.需要添加如下操作到我们的shell启动脚本中,例如用户目录下的:~/.bash_profile (或者是/etc/profile:这将对所用用户都生效)
```shell
PATH=/usr/local/pgsql/bin:$PATH
export PATH
```
如果你使用的是csh、tcsh,你需要使用如下命令:
```shell
set path = ( /usr/local/pgsql/bin $path )
```
为了使您的系统能够找到手册文档,除非您将其安装到默认搜索的位置,否则您需要在shell启动文件中添加以下内容:
```shell
MANPATH=/usr/local/pgsql/share/man:$MANPATH
export MANPATH
```
环境变量PGHOST和PGPORT向客户端应用程序指定数据库服务器的主机和端口,从而覆盖了已编译的缺省值。 如果要远程运行客户端应用程序,则每个计划使用数据库的用户都设置PGHOST会很方便。 但是,这不是必需的。 可以通过命令行选项将设置传达给大多数客户端程序。
通常情况下,当您在同一服务器上同时配置协调器和数据节点时,请注意不要为不同的组件分配相同的资源,例如侦听点(IP地址和端口号)。 否则它们将发生冲突,并且Postgres-XL将无法正确运行。
#### Supported Platforms
可以期望Postgres-XL在以下操作系统上运行:Linux(所有最新发行版),FreeBSD和Mac OSX。其他类似Unix的系统也可以运行,但目前尚未进行测试。
如果根据最近的构建的结果在已知支持的平台上有安装问题,请将其报告给<postgres-xl-bugs@lists.sourceforge.net>。 如果您有兴趣将Postgres-XL移植到新平台上,可以在<postgres-xl-developers@lists.sourceforge.net>上进行讨论。
#### Platform-specific Notes
### Installation from Source Code on Windows
#### Building with Visual C++ or the Microsoft Windows SDK
### Server Setup and Operation
本章讨论如何设置和运行数据库服务器及其与操作系统的交互。
#### The PostgreSQL User Account
与外界可以访问的任何服务器守护程序一样,建议在一个单独的用户帐户下运行PostgreSQL。 该用户帐户应仅拥有服务器管理的数据,而不应与其他守护程序共享。 (例如,使用用户nobody是个坏主意。)不建议安装该用户拥有的可执行文件,因为受感染的系统随后可能会修改其自己的二进制文件。
要将Unix用户帐户添加到系统中,请查找命令useradd或adduser。 经常使用用户名postgres,并且在本书中都使用该用户名,但是如果您愿意,可以使用其他名称。
#### Creating a Database Cluster
配置Postgres-XL群集的步骤可能很麻烦。 强烈建议您使用一个名为`pgxc-ctl`的实用程序来简化此任务,并且可以跳过本章的许多内容。 也就是说,您可以继续阅读以了解如何手动配置集群,该集群本质上描述了`pgxc-ctl`的步骤。
在执行任何操作之前,必须初始化磁盘上的数据库存储区域。 我们称其为数据库集群。 (SQL标准使用术语目录集群。)数据库集群是由运行中的数据库服务器的单个实例管理的数据库的集合。 初始化之后,数据库集群将包含一个名为postgres的数据库,这是公用程序,用户和第三方应用程序使用的默认数据库。 数据库服务器本身不需要postgres数据库存在,但是许多外部实用程序都假定它存在。 在初始化期间在每个集群中创建的另一个数据库称为template1。 顾名思义,它将用作后续创建的数据库的模板; 它不应该用于实际工作。 (有关在群集中创建新数据库的信息,请参见第22章。)
用文件系统的术语来说,数据库集群是一个目录,所有数据都将存储在该目录下。 我们称其为数据目录或数据区域。 选择存储数据的位置完全取决于您。 没有默认值,尽管/ usr / local / pgsql / data或/ var / lib / pgsql / data等位置很受欢迎。 要初始化数据库集群,请使用命令initdb,该命令与Postgres-XL一起安装。 -D选项指示数据库集群所需的文件系统位置。 您还需要为初始化的集群元素定义节点名称,例如:
```shell
$ initdb -D /usr/local/pgsql/data --nodename foo
```
请注意,您必须在登录到Postgres-XL用户帐户时执行该命令,如上一节所述。 如果要在同一服务器中配置它们,则应为每个协调器和数据节点分配单独的数据目录。
作为-D选项的替代方法,您可以设置环境变量PGDATA。
在Postgres-XL中,如果配置多个Coordinator和/或Datanode,则不能在其中共享PGDATA,必须显式指定数据目录。
--nodename对于初始化时所有节点都是必需的
另外,您可以通过pg_ctl程序运行initdb,如下所示:
```shell
$ pg_ctl -D /usr/local/pgsql/data -o '--nodename foo' initdb
```
如果您使用pg_ctl来启动和停止服务器(请参见第18.3节),则这可能会更直观,因此pg_ctl将是您用于管理数据库服务器实例的唯一命令。
initdb将尝试创建您指定的目录(如果尚不存在)。 当然,如果initdb没有在父目录中写入的权限,这将失败。 通常建议PostgreSQL用户不仅拥有数据目录,而且拥有其父目录,因此这不应该成为问题。 如果所需的父目录也不存在,那么如果祖父母目录不可写,则需要使用root特权首先创建它。 因此,该过程可能如下所示:
```shell
root# mkdir /usr/local/pgsql
root# chown postgres /usr/local/pgsql
root# su postgres
postgres$ initdb -D /usr/local/pgsql/data
```
如果数据目录存在并且已经包含文件,则initdb将拒绝运行; 这是为了防止意外覆盖现有安装。
因为数据目录包含存储在数据库中的所有数据,所以必须防止未经授权的访问,这是至关重要的。 因此,initdb撤消了除PostgreSQL用户以外的所有人的访问权限。
但是,尽管目录内容是安全的,但默认的客户端身份验证设置允许任何本地用户连接到数据库,甚至成为数据库超级用户。 如果您不信任其他本地用户,我们建议您使用initdb的-W,-pwprompt或--pwfile选项之一为数据库超级用户分配密码。 另外,指定-A md5或-A密码,以便不使用默认的信任身份验证模式。 或在运行initdb之后但在首次启动服务器之前修改生成的pg_hba.conf文件。 (其他合理的方法包括使用对等身份验证或文件系统权限来限制连接。有关更多信息,请参见第20章。)
initdb还会初始化数据库集群的默认语言环境。 通常,它将仅使用环境中的语言环境设置并将其应用于初始化的数据库。 可以为数据库指定不同的语言环境。 有关此内容的更多信息,请参见第23.1节。 特定数据库集群中使用的默认排序顺序由initdb设置,尽管您可以使用不同的排序顺序来创建新数据库,但是initdb创建的模板数据库中使用的顺序必须先删除和重新创建,才能更改。 使用C或POSIX以外的语言环境也会对性能产生影响。 因此,第一次正确选择此选项很重要。
initdb还为数据库集群设置默认字符集编码。 通常,应选择与语言环境设置匹配的语言。 有关详细信息,请参见第23.3节。
非C和非POSIX语言环境依赖于操作系统的归类库来进行字符集排序。 这控制了存储在索引中的键的顺序。 因此,群集无法通过快照还原,二进制流复制,其他操作系统或操作系统升级切换到不兼容的归类库版本。
##### Use of Secondary File Systems
除了计算机的“根”卷,许多安装都在文件系统(卷)上创建数据库集群。 如果选择这样做,则不建议尝试使用辅助卷的最顶层目录(装入点)作为数据目录。 最佳实践是在PostgreSQL用户拥有的安装点目录中创建一个目录,然后在其中创建数据目录。 这样可以避免权限问题,尤其是对于诸如pg_upgrade之类的操作,还可以确保在辅助卷脱机时清除失败。
##### Use of Network File Systems
许多安装在网络文件系统上创建其数据库群集。 有时,这是通过NFS或通过内部使用NFS的网络连接存储(NAS)设备完成的。 PostgreSQL对NFS文件系统没有任何特殊要求,这意味着它假定NFS的行为与本地连接的驱动器完全相同。 如果客户端或服务器的NFS实现不提供标准的文件系统语义,则可能会导致可靠性问题(请参阅http://www.time-travellers.org/shane/papers/NFS_considered_harmful.html)。 具体来说,延迟(异步)写入NFS服务器可能会导致数据损坏问题。 如果可能,请同步安装NFS文件系统(不进行缓存),以避免这种危险。 另外,不建议软安装NFS文件系统。
存储区域网络(SAN)通常使用NFS以外的通信协议,并且可能会或可能不会遭受此类危险。 建议您查阅有关数据一致性保证的供应商文档。 PostgreSQL不能比其使用的文件系统更可靠。
#### Starting Postgres-XL Cluster
在我们访问数据库之前,首先我们需要启动数据库服务.数据库服务程序名为`postgres`. postgres程序必须知道在哪里可以找到应该使用的数据。 这是通过-D选项完成的。 因此,启动服务器的最简单方法是:
```shell
$ postgres -D /usr/local/pgsql/data
```
这将使服务器在前台运行。 必须在登录PostgreSQL用户帐户时完成此操作。 如果没有-D,服务器将尝试使用由环境变量PGDATA命名的数据目录。 如果也未提供该变量,它将失败。
通常,最好在后台启动postgres。 为此,请使用通常的Unix Shell语法:
```shell
$ postgres -D /usr/local/pgsql/data >logfile 2>&1 &
```
如上所示,将服务器的stdout和stderr输出存储在某处很重要。 这将有助于审核和诊断问题。 (有关日志文件处理的更详尽讨论,请参见第24.3节。)
postgres程序还具有许多其他命令行选项。 有关更多信息,请参见postgres参考页和下面的第19章。
这种shell语法很快就会变得乏味。 因此,提供了包装程序pg_ctl来简化某些任务。 例如:
```shell
pg_ctl start -l logfile
```
将在后台启动服务器并将输出放入命名的日志文件中。 -D选项的含义与postgres的含义相同。 pg_ctl也能够停止服务器。
通常,您需要在计算机启动时启动数据库服务器。 自动启动脚本是特定于操作系统的。 PostgreSQL的contrib / start-scripts目录中有一些分发。 安装一个将需要root特权。
不同的系统在引导时启动守护程序的约定不同。 许多系统都有文件/etc/rc.local或/etc/rc.d/rc.local。 其他人使用init.d或rc.d目录。 无论做什么,服务器都必须由PostgreSQL用户帐户运行,而不要由root用户或任何其他用户运行。 因此,您可能应该使用su postgres -c'...'来形成命令。 例如:
```shell
su postgres -c 'pg_ctl start -D /usr/local/pgsql/data -l serverlog' #切换用户并执行命令
```
以下是一些针对特定操作系统的建议。 (在每种情况下,请确保在显示通用值的地方使用正确的安装目录和用户名。)
- 对于FreeBSD,请查看PostgreSQL源代码发行版中的contrib / start-scripts / freebsd文件。
- 在OpenBSD上,将以下行添加到文件/etc/rc.local中:
```shell
if [ -x /usr/local/pgsql/bin/pg_ctl -a -x /usr/local/pgsql/bin/postgres ]; then
su -l postgres -c '/usr/local/pgsql/bin/pg_ctl start -s -l /var/postgresql/log -D /usr/local/pgsql/data'
echo -n ' postgresql'
fi
```
- 在Linux系统上,要么添加
```shell
/usr/local/pgsql/bin/pg_ctl start -l logfile -D /usr/local/pgsql/data
```
到/etc/rc.d/rc.local或/etc/rc.local或查看PostgreSQL源代码发行版中的contrib / start-scripts / linux文件。
使用systemd时,可以使用以下服务单元文件(例如,在/etc/systemd/system/postgresql.service中):
```
[Unit]
Description=PostgreSQL database server
Documentation=man:postgres(1)
[Service]
Type=notify
User=postgres
ExecStart=/usr/local/pgsql/bin/postgres -D /usr/local/pgsql/data
ExecReload=/bin/kill -HUP $MAINPID
KillMode=mixed
KillSignal=SIGINT
TimeoutSec=0
[Install]
WantedBy=multi-user.target
```
使用Type = notify要求服务器二进制文件是使用configure --with-systemd构建的。
请仔细考虑超时设置。 截至撰写本文时,systemd的默认超时为90秒,它将终止在该时间内未通知就绪的进程。 但是,可能需要在启动时执行崩溃恢复的PostgreSQL服务器可能需要更长的时间才能准备就绪。 建议值为0禁用超时逻辑。
- 在NetBSD上,根据喜好使用FreeBSD或Linux启动脚本。
- 在Solaris上,创建一个名为/etc/init.d/postgresql的文件,其中包含以下行:
```shell
su - postgres -c "/usr/local/pgsql/bin/pg_ctl start -l logfile -D /usr/local/pgsql/data"
```
然后,在/etc/rc3.d中以S99postgresql的形式创建指向它的符号链接。
服务器运行时,其PID存储在数据目录中的文件postmaster.pid中。 这用于防止多个服务器实例在同一数据目录中运行,也可以用于关闭服务器。
如上一章所述,XL由各种组件组成。 最少的组件集是GTM,GTM代理,协调器和数据节点。 您必须配置并启动它们中的每一个。 以下各节将向您介绍如何配置和启动它们。 `pgxc_clean`和GTM-Standby在高可用性部分中进行了描述。
##### Creating Databases
您应该初始化组成Postgres-XL数据库集群系统的每个数据库。 Coordinator和Datanode都有自己的数据库,您应该初始化这些数据库。 协调器仅保存数据库目录和临时数据存储。 Datanode拥有您的大部分数据。 首先,您应该确定要运行多少个协调器/数据节点以及它们应在何处运行。 在您运行数据节点的地方运行协调器是一个很好的约定。 在这种情况下,您也应该在同一服务器上运行GTM-Proxy。 它简化了XL的配置,并有助于使每个服务器的工作量均匀。
Coordinator和Datanode都有自己的数据库,本质上都是PostgreSQL数据库。 它们是分开的,您应该分别对其进行初始化。
##### Starting a GTM
GTM为Postgres-XL数据库集群中的所有其他组件提供了全局事务管理功能。 由于GTM可以处理所有协调器和数据节点的事务要求,因此强烈建议在单独的服务器中运行它。
在启动GTM之前,您应该决定以下事项:
在哪运行GTM
因为GTM接收到所有开始/结束事务和引用序列值的请求,所以您应该在单独的服务器上运行GTM。 如果您在与Datanode或Coordinator相同的服务器上运行GTM,将很难使工作负载合理平衡。
然后,您应该确定GTM的工作目录。 在运行GTM之前,请先创建此目录。
GTM监听的地址及端口
接下来,您应该确定GTM的监听地址和端口。 侦听地址可以是从其他组件(通常是GTM代理)接收请求的IP地址或主机名。
GTM id
你可以在一个Postgres-XL集群中运行多个GTM。 例如,如果需要在高可用性环境中备份GTM,则需要运行两个GTM。 您应该为每个此类GTM赋予唯一的GTM ID。 GTM ID值以1开头。
确定后,可以使用命令initgtm初始化GTM,例如:
```shell
$ initgtm -Z gtm -D /usr/local/pgsql/data_gtm
```
可以在initgtm初始化的数据文件夹中的gtm.conf中修改与GTM相关的所有参数。
然后,您可以按照以下步骤启动GTM:
```shell
$ gtm -D /usr/local/pgsql/data_gtm
```
其中-D选项指定GTM的工作目录。
或者,可以使用gtm_ctl启动GTM,例如:
```shell
$ gtm_ctl -Z gtm start -D /usr/local/pgsql/data_gtm
```
##### Starting a GTM-Proxy
GTM代理不是Postgres-XL群集的必需组件,但可用于在GTM和群集节点之间对消息进行分组,从而减少了工作量并减少了通过网络交换的软件包数量。
如上一节所述,GTM代理需要自己的侦听地址,端口,工作目录和GTM代理ID,它们应该是唯一的,并且应以一个开头。 另外,您应该确定要运行多少个工作线程。 您还应该使用GTM的地址和端口启动GTM代理。
然后,您首先需要使用initgtm初始化GTM代理,例如:
```shell
$ initgtm -Z gtm_proxy -D /usr/local/pgsql/data_gtm_proxy
```
可以在initgtm初始化的数据文件夹中的gtm_proxy.conf中修改与GTM代理相关的所有参数。
然后,您可以启动GTM代理,如下所示:
```shell
$ gtm_proxy -D /usr/local/pgsql/data_gtm_proxy
```
其中-D指定GTM-Proxy的工作目录。
或者,您可以使用gtm_ctl启动GTM代理,如下所示:
```shell
$ gtm_ctl start -Z gtm_proxy -D /usr/local/pgsql/data_gtm_proxy
```
##### Configuring Datanodes
在启动Coordinator或Datanode之前,必须对其进行配置。 您可以通过编辑位于initdb命令中-D选项指定的工作目录中的postgresql.conf文件来配置Coordinator或Datanode。
Datanode几乎是本机PostgreSQL,具有一些扩展。 数据节点的postgresql.conf中的其他选项如下:
max_connections
该值不仅是您希望与每个协调器建立的连接数。 每个协调器后端都有机会连接到所有Datanode。 您应指定整个协调员可以接受的总连接数。 例如,如果您有五个协调器,并且每个协调器可以接受40个连接,则应将200指定为该参数值。
max_prepared_transactions
即使您的应用程序不打算发出PREPARE TRANSACTION,当涉及多个Datanode时,协调器也可能在内部发出此消息。 您应该将此参数指定为与max_connections相同的值。
pgxc_node_name
GTM需要标识此参数指定的每个数据节点。 该值应该是唯一的,并以1开头。
port
由于Coordinator和Datanode都可以在同一服务器上运行,因此您可能想为Datanode分配单独的端口号。
gtm_port
按照gtm_proxy或gtm_ctl中的-p选项中的指定,指定GTM代理的端口号。
gtm_host
按照gtm_proxy或gtm_ctl中的-h选项中的指定,指定GTM-Proxy的主机名或IP地址。
shared_queues
对于查询中发生的某些联接,可能需要将一个Datanode的数据与另一Datanode的数据联接。 Postgres-XL为此使用共享队列。 在执行期间,每个Datanode都知道它是否需要产生或使用元组,或者两者都需要。
注意,即使是单个查询,也可能会使用多个shared_queues。 因此,应在考虑到其可以接受的连接数量以及同时发生的此类连接的预期数量的基础上设置一个值。
shared_queue_size
此参数设置每个分配的共享队列的大小。
##### Configuring Coordinators
尽管协调器和数据节点共享相同的二进制文件,但由于其功能,它们的配置还是有些不同。
max_connections
您不必考虑其他协调器或数据节点。 只需指定协调器从应用程序接受的连接数即可。
max_prepared_transactions
至少指定集群中的协调器总数。
pgxc_node_name
GTM需要标识此参数指定的每个数据节点。
port
由于Coordinator和Datanode都可以在同一服务器上运行,因此您可能想为Coordinator分配单独的端口号。 使用PostgreSQL侦听端口的默认值可能会很方便。
gtm_port
按照gtm_proxy或gtm_ctl中的-p选项中的指定,指定GTM代理的端口号。
gtm_host
按照gtm_proxy或gtm_ctl中的-h选项中的指定,指定GTM-Proxy的主机名或IP地址。
pooler_port
指定池管理器应使用的端口号。 这不得与此主机上使用的任何其他服务器端口相冲突。
max_pool_size
协调器将与Datanode的连接作为池进行维护。 此参数指定协调器维护的最大连接数。 **将远程节点的max_connection值指定为该参数值。**
min_pool_size
这是池管理器维护的协调器到远程节点连接的最小数目。 通常指定1。
pool_conn_keepalive
此参数指定保持连接存活的时间。 如果超过此数量,则池管理器将丢弃该连接。 该参数在多租户环境中很有用,在该环境中可以使用到许多不同数据库的许多连接,以便清理空闲的连接。 这对于在有未知内存泄漏的情况下偶尔自动关闭连接很有用,以便可以释放该内存。
pool_maintenance_timeout
此参数指定执行池维护之前要等待的时间。 在此类维护期间,旧的空闲连接将被丢弃。 该参数在多租户环境中很有用,在该环境中可以使用到许多不同数据库的许多连接,以便清理空闲的连接。
remote_query_cost
此参数指定设置远程查询以获得远程数据的成本开销。 计划者在成本查询中使用它。
network_byte_cost
此参数用于查询成本计划中,以估计行运输和基于预期数据大小获取远程数据所涉及的成本。 行运输成本高昂,并增加了延迟,因此此设置有助于支持使行运输最小化的计划。
sequence_range
此参数用于一次从GTM获取多个序列值。 这大大加快了目标表使用序列的COPY和INSERT SELECT操作。 Postgres-XL不会一次使用全部金额,但是如果在同一会话的较短时间内完成许多请求,则随着时间的推移,请求大小会增加。 短时间后,没有任何序列请求,请减小到1。请注意,如果在CREATE SEQUENCE或ALTER SEQUENCE中使用了CACHE子句,则此处的任何设置都会被覆盖。
max_coordinators
这是集群中可以配置的最大协调器数。 如果不打算在集群运行时添加更多的协调器,请指定确切的数字;如果希望动态调整集群的大小,请指定更大的数目。 每个插槽大约需要140字节的共享内存。
max_datanodes
这是集群中配置的最大数据节点数。 如果不打算在集群运行时添加更多的Datanode,请指定确切的数字;如果希望动态调整集群的大小,请指定更大的数目。 每个插槽大约需要140字节的共享内存。
enforce_two_phase_commit
在涉及ON COMMIT动作或临时对象的事务上强制使用两阶段提交。 使用自动提交而不是两阶段提交可能会破坏数据一致性,因此使用后果自负。
##### Starting Datanodes
现在,您可以启动Postgres-XL,Datanode和Coordinator的中心组件。 如果您熟悉启动PostgreSQL数据库服务器,则此步骤与PostgreSQL非常相似。
您可以按以下方式启动一个Datanode:
```shell
$ postgres --datanode -D /usr/local/pgsql/data
```
--datanode指定postgres应该作为Datanode运行。 如果群集使用多个服务器之间的节点,则可能需要指定-i postgres以接受来自TCP / IP连接的连接,或编辑pg_hba.conf。
##### Starting Coordinators
您可以按以下方式启动协调器:
```shell
$ postgres --coordinator -D /usr/local/pgsql/Datanode
```
--coordinator指定postgres应作为协调器运行。 如果群集使用多个服务器之间的节点,则可能需要指定-i postgres以接受来自TCP / IP连接的连接,或编辑pg_hba.conf。
##### Server Start-up Failures
有几种常见原因可能导致服务器无法启动。 检查服务器的日志文件,或手动启动它(不重定向标准输出或标准错误),看看出现了什么错误消息。 下面我们将更详细地解释一些最常见的错误消息。
```
LOG: could not bind IPv4 address "127.0.0.1": Address already in use
HINT: Is another postmaster already running on port 5432? If not, wait a few seconds and retry.
FATAL: could not create any TCP/IP sockets
```
这通常意味着它的含义:您试图在已运行同一端口的另一台服务器上启动另一台服务器。 但是,如果内核错误消息不是尚未使用的地址或该地址的某些变体,则可能存在其他问题。 例如,尝试在保留端口号上启动服务器可能会出现类似以下内容:
```
$ postgres -p 666
LOG: could not bind IPv4 address "127.0.0.1": Permission denied
HINT: Is another postmaster already running on port 666? If not, wait a few seconds and retry.
FATAL: could not create any TCP/IP sockets
```
如下的消息:
```
FATAL: could not create shared memory segment: Invalid argument
DETAIL: Failed system call was shmget(key=5440001, size=4011376640, 03600).
```
另一个错误:
```
FATAL: could not create semaphores: No space left on device
DETAIL: Failed system call was semget(5440126, 17, 03600).
```
并不意味着您已经用完了磁盘空间。 这意味着您的内核对System V信号量的限制小于PostgreSQL要创建的数目。 如上所述,您可以通过减少允许的连接数(max_connections)启动服务器来解决此问题,但最终您将希望增加内核限制。
如果收到“非法系统调用”错误,则可能是内核根本不支持共享内存或信号灯。 在这种情况下,您唯一的选择是重新配置内核以启用这些功能。
有关配置System V IPC设施的详细信息,请参见第18.4.1节。
##### Client Connection Propblems
尽管客户端可能发生的错误情况千差万别,并且取决于应用程序,但其中一些错误可能与服务器的启动方式直接相关。 除以下所示条件外,其他应用程序应记录在案。
```
psql: could not connect to server: Connection refused
Is the server running on host "server.joe.com" and accepting
TCP/IP connections on port 5432?
```
这是常见的“当前客户端连接不到服务器”失败。 尝试进行TCP / IP通信时,看起来像上面的样子。 一个常见的错误是忘记将服务器配置为允许TCP / IP连接。
另外,尝试与本地服务器进行Unix域套接字通信时,您会得到以下信息:
```
psql: could not connect to server: No such file or directory
Is the server running locally and accepting
connections on Unix domain socket "/tmp/.s.PGSQL.5432"?
```
最后一行对于验证客户端是否尝试连接到正确的位置很有用。 如果实际上那里没有服务器在运行,则内核错误消息通常是“连接被拒绝”或“没有这样的文件或目录”,如图所示。 (很重要的一点是,必须意识到在这种情况下Connection被拒绝并不意味着服务器收到了您的连接请求并拒绝了它。这种情况将产生不同的消息,如第20.4节所示。)其他错误消息(例如Connection超时)可能 表示更根本的问题,例如缺乏网络连接。
#### Managing Kernel Resources
PostgreSQL有时可能会耗尽各种操作系统资源的限制,尤其是当服务器的多个副本在同一系统上或在非常大的安装中运行时。 本节说明了PostgreSQL使用的内核资源以及解决与内核资源消耗有关的问题可采取的步骤。
##### Shared Memory and Semaphores
PostgreSQL要求操作系统提供进程间通信(IPC)功能,特别是共享内存和信号量。 Unix派生的系统通常提供“System V” IPC,“ POSIX” IPC或两者。 Windows具有自己的这些功能的实现,此处不再讨论。
这些功能的完全缺乏通常通过服务器启动时出现“非法系统调用”错误来表明。 在这种情况下,别无选择,只能重新配置内核。 没有它们,PostgreSQL将无法工作。 但是,这种情况在现代操作系统中很少见。
启动服务器后,PostgreSQL通常会分配少量的System V共享内存,以及大量的POSIX(mmap)共享内存。 另外,在服务器启动时会创建大量的信号量,可以是System V或POSIX风格。 当前,POSIX信号量用于Linux和FreeBSD系统,而其他平台则使用System V信号量。
在PostgreSQL 9.3之前,仅使用System V共享内存,因此启动服务器所需的System V共享内存量要大得多。 如果您正在运行服务器的旧版本,请查阅服务器版本的文档。
System V IPC功能通常受系统范围分配限制的约束。 当PostgreSQL超出这些限制之一时,服务器将拒绝启动,并应留下指示性错误消息,说明问题及其处理方法。 (另请参见第18.3.8节。)相关内核参数在不同系统中的名称保持一致。 表18.1给出了概述。 但是,设置它们的方法各不相同。 以下是一些平台的建议。
**Table 18.1. System V IPC Parameters**
| Na me | Description | Values needed to run one PostgreSQL instance |
| ----------------------- | ---------------------------------- | ------------------------------------------------------------ |
| `SHMMAX` | 共享内存段的最大大小(字节) | 至少1kB,但默认值通常更高 |
| `SHMMIN` | 共享内存段的最小大小(字节) | 1 |
| `SHMALL` | 可用共享内存总量(字节或页) | 如果是字节,则与`SHMMAX`相同;如果是页,则与`ceil(SHMMAX / PAGE_SIZE)`相同,并为其他应用程序提供空间 |
| `SHMSEG` | 每个进程的最大共享内存段数 | 只需要1个细分,但默认设置要高得多 |
| `SHMMNI` | 全系统共享内存段的最大数量 | 像“ SHMSEG”,还有其他应用程序的空间 |
| `SEMMNI` | 信号灯标识符的最大数量(即,集合) | 至少`ceil((max_connections + autovacuum_max_workers + max_worker_processes + 5)/ 16)`以及其他应用程序的空间 |
| `SEMMNS` | 全系统最大信号量 | `ceil((max_connections + autovacuum_max_workers + max_worker_processes + 5)/ 16)* 17`以及其他应用程序的空间 |
| `SEMMSL` | 每组最大信号量 | 至少17个 |
| `SEMMAP` | 信号量中的条目数 | see text |
| `SEMVMX` | 信号量最大值 | 至少1000(默认值通常是32767;除非有必要,否则请不要更改) |
PostgreSQL需要为服务器的每个副本提供几个字节的System V共享内存(在64位平台上通常为48个字节)。 在大多数现代操作系统上,可以轻松分配此数量。 但是,如果您正在运行服务器的许多副本,或者其他应用程序也在使用System V共享内存,则可能有必要增加SHMALL,SHMALL是系统范围内System V共享内存的总量。 请注意,在许多系统上,SHMALL以页而不是字节为单位进行度量。
引起问题的可能性较小的是共享内存段的最小大小(SHMMIN),对于PostgreSQL,最大大小应约为32个字节(通常仅为1)。 除非您的系统将它们设置为零,否则系统范围(SHMMNI)或每个进程(SHMSEG)的最大段数不太可能引起问题。
使用System V信号量时,PostgreSQL每16个一组使用一个允许的连接数(max_connections),允许的autovacuum worker进程(autovacuum_max_workers)和允许的后台进程(max_worker_processes)一个信号量。每个这样的集合还将包含第17个信号量,其中包含“魔术数”,以检测与其他应用程序使用的信号量集的冲突。系统中的最大信号量由SEMMNS设置,因此必须至少与max_connections加上autovacuum_max_workers加上max_worker_processes一样高,再加上每16个允许的连接数加上worker数(请参见表18.1中的公式)。参数SEMMNI确定一次可以在系统上存在的信号量集的数量限制。因此,此参数必须至少为ceil((max_connections + autovacuum_max_workers + max_worker_processes + 5)/ 16)。减少允许的连接数是一种临时性的解决方法,用于解决故障,在功能semget中,这些故障通常被混淆为“设备上无空间”。