PostgreSQL 16 Administration Cookbook 读书笔记:第7章 Database Administration

编写一个要么完全成功要么完全失败的脚本

事务(transaction)可以实现all or nothing。不过这里指的是psql的---single-transaction选项。可以实现transaction wrapper:

此选项只能与一个或多个 -c 和/或 -f 选项组合使用。它会导致 psql 在第一个此类选项之前发出 BEGIN 命令,并在最后一个此类选项之后发出 COMMIT 命令,从而将所有命令包装到单个事务中。这确保所有命令要么成功完成,要么不应用任何更改。

还有一些子句可以保证创建和删除时避免报错:

  • DROP ... IF EXISTS
  • CREATE OR REPLACE ... :无需删除依赖对象的对象,如function,procedure,view,rule,language,aggregate,type,materialized view
    • CREATE ... IF NOT EXISTS:CREATE OR REPLACE不支持的对象,基本就在这一类中

💡 索引不包含在SQL标准中,这是因为SQL 标准侧重于数据定义、操作、约束和事务,即逻辑层面。而索引属于物理实现。

PG中的事务是平(flat)的,不支持嵌套事务。

下例中,最终t1和t2成功创建,原因是COMMIT与第一个BEGIN匹配第二个COMMIT被抛弃。

sql 复制代码
BEGIN;
	create table t1(a int);
	BEGIN;
		create table t2(a int);
	COMMIT;
ROLLBACK;

嵌套事务可以间接的用SAVEPOINT实现:

sql 复制代码
BEGIN;
SAVEPOINT sp1;
-- Do something
ROLLBACK TO SAVEPOINT sp1;  -- if `Do something` failed, rollback to sp1 without rolling back the entire transaction
RELEASE SAVEPOINT sp1;      -- if `Do something` succeed, optional, remove the savepoint marker
COMMIT;

编写一个在第一次出错时退出的 psql 脚本

这里指的是psql的ON_ERROR_STOP选项:

默认情况下,命令处理在发生错误后会继续。当此变量设置为 on 时,处理将立即停止。在交互模式下,psql 将返回到命令提示符;否则,psql 将退出并返回错误代码 3,以区分此情况与致命错误情况(使用错误代码 1 报告)。无论哪种情况,任何当前正在运行的脚本(顶级脚本(如果有)以及它可能调用的任何其他脚本)都将立即终止。

sql 复制代码
postgres=# \set
AUTOCOMMIT = 'on'
...
ON_ERROR_STOP = 'off'
...

使用 psql 变量

参见这里

sql 复制代码
postgres=# \set table_name t1
postgres=# \echo :table_name
t1
postgres=# \set tabname :table_name
postgres=# \echo :tabname
t1
                       ^
postgres=# create table :tabname(a int);
CREATE TABLE

通过命令行设置,-v表示variable:

sql 复制代码
$ psql -v tabname=t1
psql (16.9)
Type "help" for help.

postgres=# \echo :tabname
t1

将查询输出放入 psql 变量

利用psql的gset元命令。

\gset [PREFIX]

execute query and store result in psql variables

例:

sql 复制代码
sampledb=> select * from employees limit 1 \gset
sampledb=> \set
...
department_id = '90'
email = 'SKING'
employee_id = '100'
first_name = 'Steven'
hire_date = '2003-06-17'
job_id = 'AD_PRES'
last_name = 'King'
phone_number = '515.123.4567'
salary = '24000.00'

编写一个条件 psql 脚本

sql 复制代码
Conditional
  \if EXPR               begin conditional block
  \elif EXPR             alternative within current conditional block
  \else                  final alternative within current conditional block
  \endif                 end conditional block

官网示例:

sql 复制代码
-- check for the existence of two separate records in the database and store
-- the results in separate psql variables
SELECT
    EXISTS(SELECT 1 FROM customer WHERE customer_id = 123) as is_customer,
    EXISTS(SELECT 1 FROM employee WHERE employee_id = 456) as is_employee
\gset
\if :is_customer
    SELECT * FROM customer WHERE customer_id = 123;
\elif :is_employee
    \echo 'is not a customer but is an employee'
    SELECT * FROM employee WHERE employee_id = 456;
\else
    \if yes
        \echo 'not a customer or employee'
    \else
        \echo 'this will never print'
    \endif
\endif

我的例子:

sql 复制代码
sampledb=> select (count(*)>100) as emp_gt_100 from employees \gset
sampledb=> \echo :emp_gt_100
t
\if :emp_gt_100
\echo Employee count is greater than 100
\else
\echo Employee count is not greater than 100
\endif

💡 psql 条件表达式后只接受布尔值。

调查 psql 错误

psql元命令errverbose:

以最大详细程度重复最新的服务器错误消息,就像将 VERBOSITY 设置为verbose 并将 SHOW_CONTEXT 设置为always一样。

sql 复制代码
sampledb=> select abc from employees;
ERROR:  column "abc" does not exist
LINE 1: select abc from employees;
               ^
sampledb=> \errverbose
ERROR:  42703: column "abc" does not exist
LINE 1: select abc from employees;
               ^
LOCATION:  errorMissingColumn, parse_relation.c:3722

VERBOSITY 和 SHOW_CONTEXT 也可以单独设置,以下为默认值:

sql 复制代码
sampledb=> \set
SHOW_CONTEXT = 'errors'
VERBOSITY = 'verbose'

SHOW_CONTEXT

此变量可以设置为 never、errors 或 always 来控制是否在服务器消息中显示 CONTEXT 字段。默认值为 error(意味着上下文会显示在错误消息中,但不会在通知或警告消息中显示)。当 VERBOSITY 设置为 terse 或 sqlstate 时,这个设置无效。(另见 \errverbose,当你想得到刚刚收到的错误的详细版本时使用。)


VERBOSITY

此变量可以设置为 default、verbose、terse 或 sqlstate 值,以控制错误报告的详细程度。(另请参阅 \errverbose,用于获取刚刚收到的错误的详细版本。)

另请参见:PostgreSQL Error CodesPostgreSQL source code

使用有用的信息设置 psql 提示符

psql 发出的提示符可以根据您的偏好进行自定义。PROMPT1、PROMPT2 和 PROMPT3 三个变量包含描述提示符外观的字符串和特殊转义序列。PROMPT1 是 psql 请求新命令时发出的常规提示符。当在命令输入过程中需要更多输入时,例如,由于命令未以分号结尾或引号未闭合,则会发出PROMPT2 。当您运行 SQL COPY FROM STDIN 命令并且需要在终端上输入行值时,会发出PROMPT3 。

所选提示符变量的值将按原样打印,除非遇到百分号 (%)。详见这里

sql 复制代码
sampledb=> \set
...
PROMPT1 = '%/%R%x%# '
PROMPT2 = '%/%R%x%# '
PROMPT3 = '>> '
...

使用 pgAdmin 执行 DBA 任务

略。

定期后台任务执行

pgAgent 是 PostgreSQL 的作业调度程序。pgAgent 使用 pgAdmin 进行管理。pgAdmin 文档包含如何在 PostgreSQL 系统中设置和使用 pgAgent 的详细信息。

简单来说,pgAgent是运行在unix上的一个daemon,pgAdmin可以与其交互。详见这里

安装:

sql 复制代码
sudo yum install pgagent_16
psql -c "create extension pgagent"

其中的函数如下:

sql 复制代码
postgres=# \dx+ pgagent
                                                         Objects in extension "pgagent"
                                                               Object description
-------------------------------------------------------------------------------------------------------------------------------------------------
 function pgagent.pga_exception_trigger()
 function pgagent.pgagent_schema_version()
 function pgagent.pga_is_leap_year(smallint)
 function pgagent.pga_job_trigger()
 function pgagent.pga_next_schedule(integer,timestamp with time zone,timestamp with time zone,boolean[],boolean[],boolean[],boolean[],boolean[])
 function pgagent.pga_schedule_trigger()
 sequence pgagent.pga_exception_jexid_seq
 sequence pgagent.pga_jobclass_jclid_seq
 sequence pgagent.pga_job_jobid_seq
 sequence pgagent.pga_joblog_jlgid_seq
 sequence pgagent.pga_jobstep_jstid_seq
 sequence pgagent.pga_jobsteplog_jslid_seq
 sequence pgagent.pga_schedule_jscid_seq
 table pgagent.pga_exception
 table pgagent.pga_job
 table pgagent.pga_jobagent
 table pgagent.pga_jobclass
 table pgagent.pga_joblog
 table pgagent.pga_jobstep
 table pgagent.pga_jobsteplog
 table pgagent.pga_schedule
(21 rows)

对多个表执行操作

其实就是循环+动态脚本。

使用psql元命令gexec和格式化函数format

format类似于C语言函数sprintf。

示例参见gexec in psql: PostgreSQL poweruser practice

在表上添加/删除列

sql 复制代码
sampledb=> create table t1(a int);
CREATE TABLE
sampledb=> insert into t1 values(1);
INSERT 0 1
sampledb=> alter table t1 add column b varchar(10);
ALTER TABLE
sampledb=> alter table t1 add column c varchar(10) default 'abc';
ALTER TABLE
sampledb=> \d t1
                                  Table "hr.t1"
 Column |         Type          | Collation | Nullable |         Default
--------+-----------------------+-----------+----------+--------------------------
 a      | integer               |           |          |
 b      | character varying(10) |           |          |
 c      | character varying(10) |           |          | 'abc'::character varying

sampledb=> select * from t1;
 a | b |  c
---+---+-----
 1 |   | abc
(1 row)

sampledb=> alter table t1 drop column c;
ALTER TABLE
sampledb=> alter table t1 drop column b;
ALTER TABLE

更改列的数据类型

基本语法:

sql 复制代码
ALTER TABLE ... ALTER COLUMN ... SET DATA TYPE

此形式更改表列的类型。涉及该列的索引和简单表约束将通过重新解析原始提供的表达式自动转换为使用新的列类型。可选的 COLLATE 子句指定新列的排序规则;如果省略,则排序规则为新列类型的默认排序规则。可选的 USING 子句指定如何根据旧列值计算新列值;如果省略,则默认转换与从旧数据类型到新类型的赋值转换相同。如果没有从旧类型到新类型的隐式转换或赋值转换,则必须提供 USING 子句。

使用此形式时,列的统计信息将被删除,因此建议随后在表上运行 ANALYZE。

示例参见PostgreSQL - Change Column TypePostgreSQL Change Column Type

更改枚举数据类型的定义

讲的是ALTER TYPE命令。

示例针对Enumerated TypesComposite Type也适用。

示例:

sql 复制代码
sampledb=> CREATE TYPE weekdays AS ENUM ('sun', 'mon', 'tue','wes', 'Fri', 'sat');
CREATE TYPE
sampledb=> ALTER TYPE weekdays RENAME VALUE 'wes' to 'wed';
ALTER TYPE
sampledb=> ALTER TYPE weekdays ADD VALUE 'thu' after 'wed';
ALTER TYPE
sampledb=> ALTER TYPE weekdays RENAME VALUE 'Fri' to 'fri';
ALTER TYPE

sampledb=> \dT+ weekdays
                                      List of data types
 Schema |   Name   | Internal name | Size | Elements | Owner | Access privileges | Description
--------+----------+---------------+------+----------+-------+-------------------+-------------
 hr     | weekdays | weekdays      | 4    | sun     +| hr    |                   |
        |          |               |      | mon     +|       |                   |
        |          |               |      | tue     +|       |                   |
        |          |               |      | wed     +|       |                   |
        |          |               |      | thu     +|       |                   |
        |          |               |      | fri     +|       |                   |
        |          |               |      | sat      |       |                   |
(1 row)

sampledb=> SELECT enumlabel
FROM pg_enum
WHERE enumtypid = 'weekdays'::regtype
ORDER BY enumsortorder;
 enumlabel
-----------
 sun
 mon
 tue
 wed
 thu
 fri
 sat
(7 rows)

异步添加约束以增加并发度

原理见这里

语法为:ADD table_constraint [ NOT VALID ]

此形式使用与 CREATE TABLE 相同的约束语法,并附加 NOT VALID 选项(目前仅适用于外键约束和 CHECK 约束),向表添加新的约束。

通常,此形式将扫描表以验证表中所有现有行是否满足新约束。但如果使用 NOT VALID 选项,则会跳过此可能耗时的扫描。该约束仍将强制执行后续插入或更新操作(也就是说,除非引用表中存在匹配的行(对于外键),否则插入或更新操作将失败;除非新行符合指定的检查条件,否则插入或更新操作将失败)。但是,数据库不会假定该约束适用于表中的所有行,直到使用 VALIDATE CONSTRAINT 选项进行验证为止。

虽然大多数形式的 ADD table_constraint 都需要 ACCESS EXCLUSIVE 锁,但 ADD FOREIGN KEY 只需要 SHARE ROW EXCLUSIVE 锁。请注意,ADD FOREIGN KEY 除了获取声明约束的表上的锁之外,还会获取被引用表上的 SHARE ROW EXCLUSIVE 锁。

在分区表中添加唯一键或主键约束时,会受到其他限制;请参阅 CREATE TABLE。此外,目前分区表上的外键约束可能无法声明为 NOT VALID。

ADD table_constraint NOT VALID 的目的如下:

扫描大型表以验证新的外键或检查约束可能需要很长时间,并且对该表的其他更新将被锁定,直到提交 ALTER TABLE ADD CONSTRAINT 命令。NOT VALID 约束选项的主要目的是减少添加约束对并发更新的影响。使用 NOT VALID,ADD CONSTRAINT 命令不会扫描表并可以立即提交。之后,可以发出 VALIDATE CONSTRAINT 命令来验证现有行是否满足约束。验证步骤不需要锁定并发更新,因为它知道其他事务将对其插入或更新的行强制执行约束;只需要检查预先存在的行。因此,验证仅获取正在修改的表上的 SHARE UPDATE EXCLUSIVE 锁。 (如果约束是外键,则约束引用的表上也需要行共享锁。)除了提高并发性之外,在已知表包含预先存在的违规情况的情况下,使用 NOT VALID 和 VALIDATE CONSTRAINT 也很有用。一旦约束到位,就不能插入新的违规,并且可以在 VALIDATE CONSTRAINT 最终成功之前轻松地纠正现有问题。

添加/删除模式

sql 复制代码
postgres=# create schema if not exists test;
CREATE SCHEMA
postgres=# grant select on all tables in schema test to public;
GRANT
postgres=# drop schema test;
DROP SCHEMA

在模式之间移动对象

语法为:

sql 复制代码
ALTER TABLE ... SET SCHEMA
-- ALTER TABLE myschema.distributors SET SCHEMA yourschema;

此形式将表移动到另一个模式。表列所拥有的关联索引、约束和序列也将被移动。

添加/删除表空间

在PG中,表空间就是一个目录:

sql 复制代码
postgres=# \db+
                                     List of tablespaces
    Name    |  Owner   |    Location    | Access privileges | Options |  Size   | Description
------------+----------+----------------+-------------------+---------+---------+-------------
 pg_default | postgres |                |                   |         | 484 MB  |
 pg_global  | postgres |                |                   |         | 637 kB  |
 ts_a       | postgres | /data/tbs/ts_a |                   |         | 6 bytes |
 ts_b       | postgres | /data/tbs/ts_b |                   |         | 6 bytes |
(4 rows)

详见CREATE TABLESPACEDROP TABLESPACE

修改表空间的位置其实就是使用符号链接指向新位置即可。

还可以修改表空间的owner,修改数据库或用户的默认表空间。

在表空间之间移动对象

语法为:

sql 复制代码
ALTER TABLE ... SET TABLESPACE ...
-- 索引和表可以位于不同的表空间
ALTER INDEX ... SET TABLESPACE ...
REINDEX ... TABLESPACE ...

访问其他 PostgreSQL 数据库中的对象

使用postgres_fdw扩展。

或者定义函数,然后在函数中直接连接另一个库,然后操作。

访问其他外部数据库中的对象

参见Foreign data wrappers中的Specific SQL Database Wrappers,如oracle_fdw,mysql_fdw等。

使视图可更新

PG中的视图不用特别声明,如果足够简单,本身就是支持插入,更新和删除的。

所谓足够简单的具体含义,请参见Updatable views以及CREATE VIEW中的Updatable Views,特别是后者,包含具体的定义和示例。

具体要求为:

  • 视图的 FROM 列表中必须只有一个条目,该条目必须是表或其他可更新视图。
  • 视图定义顶层不得包含 WITH、DISTINCT、GROUP BY、HAVING、LIMIT 或 OFFSET 子句。
  • 视图定义顶层不得包含集合运算(UNION、INTERSECT 或 EXCEPT)。
  • 视图的选择列表不得包含任何聚合函数、窗口函数或集合返回函数。

其他示例参考Creating PostgreSQL Updatable ViewsPostgreSQL - Create updatable Views

使用物化视图

略。

使用 GENERATED 数据列

详见Generated Columns

生成列是一种特殊的列,它总是根据其他列计算得出。因此,它对于列的作用就如同视图对于表的作用一样。生成列有两种:存储列和虚拟列。存储列在写入(插入或更新)时进行计算,并像普通列一样占用存储空间。虚拟生成列不占用存储空间,在读取时进行计算。因此,虚拟生成列类似于视图,而存储生成列类似于物化视图(除了它总是自动更新之外)。PostgreSQL 目前仅实现了存储生成列。

要创建生成列,请在 CREATE TABLE 中使用 GENERATED ALWAYS AS 子句,例如:

sql 复制代码
CREATE TABLE people (
    ...,
    height_cm numeric,
    height_in numeric GENERATED ALWAYS AS (height_cm / 2.54) STORED
);

PG还支持Identity Columns

标识列是一种由隐式序列自动生成的特殊列。它可用于生成键值。

要创建标识列,请在 CREATE TABLE 语句中使用 GENERATED ... AS IDENTITY 子句,例如:

sql 复制代码
CREATE TABLE people (
    id bigint GENERATED ALWAYS AS IDENTITY,
    ...,
);

使用数据压缩

PostgreSQL 提供多种类型的数据压缩:

  • 长数据值自动压缩 (TOAST)
  • 提供压缩数据类型的扩展(例如 JSON)
  • WAL 文件压缩
  • 转储文件压缩
  • 基础备份压缩
  • SSL 压缩(SSL 压缩被认为是不安全的,因此仅在私有网络中使用)
  • GiST 和 SP-GiST 索引压缩
  • Btree 索引压缩(也称为重复数据删除)
sql 复制代码
postgres=# create table t1(a varchar(512) compression lz4);
CREATE TABLE

postgres=# show default_toast_compression;
 default_toast_compression
---------------------------
 pglz
(1 row)

来看下 TOAST(The Oversized-Attribute Storage Technique)的概念。

PostgreSQL 使用固定的页面大小(通常为 8 KB),并且不允许元组跨越多个页面。因此,无法直接存储非常大的字段值。为了克服这一限制,大字段值会被压缩和/或拆分成多个物理行。这个过程对用户来说是透明的,对大多数后端代码的影响很小。这项技术被亲切地称为 TOAST(意为"自切片面包以来最棒的东西")。TOAST 基础架构还用于改进内存中大数据值的处理。

只有某些数据类型支持 TOAST --- 无需对无法生成大字段值的数据类型施加开销。要支持 TOAST,数据类型必须具有可变长度 (varlena) 表示,其中,通常,任何存储值的第一个四字节字包含该值的总长度(以字节为单位)(包括其自身)。

TOAST 占用了 varlena 长度字的两位(大端机器上的高位,小端机器上的低位),从而将可 TOAST 数据类型的任何值的逻辑大小限制为 1 GB(230 - 1 字节)。

TOAST 管理代码支持四种不同的策略,用于在磁盘上存储可 TOAST 列,详见这里

  • PLAIN 策略禁止压缩或行外存储。对于不可 TOAST 数据类型的列,这是唯一可行的策略。
  • EXTENDED 策略允许压缩和行外存储。这是大多数可 TOAST 数据类型的默认策略。系统将首先尝试压缩,如果行仍然过大,则尝试行外存储。
  • EXTERNAL 策略允许行外存储,但不允许压缩。使用 EXTERNAL 策略可以加快对宽文本和 bytea 列的子字符串操作速度(但会增加存储空间),因为这些操作经过优化,在行外值未压缩的情况下,仅获取所需的部分。
  • MAIN 策略允许压缩,但不允许行外存储。(实际上,此类列仍会进行行外存储,但只有在没有其他方法使行足够小以适应页面大小时,才会将其作为最后的手段。)
相关推荐
爱吃烤鸡翅的酸菜鱼10 小时前
IDEA高效开发:Database Navigator插件安装与核心使用指南
java·开发语言·数据库·编辑器·intellij-idea·database
m0_6530313611 小时前
PostgreSQL技术大讲堂 - 第97讲:PG数据库编码和区域(locale)答疑解惑
数据库·postgresql
kfepiza1 天前
PostgreSQL入门笔记250718
postgresql
Dontla2 天前
Postgres介绍(PostgreSQL)(开源对象关系型数据库管理系统)(与Mysql对比)
数据库·postgresql·开源
__风__4 天前
PostgreSQL ExecInitIndexScan 函数解析
数据库·postgresql
中文很快乐4 天前
postgreSQL的sql语句
数据库·sql·postgresql
····懂···4 天前
如何成为 PostgreSQL 中级专家
数据库·postgresql
dingdingfish4 天前
PostgreSQL 16 Administration Cookbook 读书笔记:第6章 Security
postgresql·database·security·administration·cookbook
小至尖尖5 天前
FastCDC 项目启动玩玩 😁😁😁😁
postgresql·sql优化