日常数据库维护工作
定期备份,定期”清理“数据库,周期性的日志文件管理
check_postgres可用于检测数据库的健康并报告异常情况
1. 日常清理
PostgreSQL数据库要求周期性的清理维护。对于很多安装,让自动清理守护进程来执行清理已经足够
1.1. 清理的基础知识
PostgreSQL的VACUUM命令出于几个原因必须定期处理每一个表:
1. 恢复或重用被已更新或已删除行所占用的磁盘空间。
2. 更新被PostgreSQL查询规划器使用的数据统计信息。
3. 更新可见性映射,它可以加速只用索引的扫描。
4. 保护老旧数据不会由于事务ID回卷或多事务ID回卷而丢失。
有 两 种VACUUM的变体:标准VACUUM和VACUUM FULL。
VACUUM FULL可以收回更多磁盘空间但是运行起来更慢。VACUUM FULL要求在其工作的表上得到一个排他锁,因此无法和对此表的其他使用并行。
标准形式的VACUUM可以和生产数据库操作并行运行(SELECT、INSERT、UPDATE和DELETE等命令将继续正常工作,但在清理期间你无法使
用ALTER TABLE等命令来更新表的定义)。
VACUUM会产生大量I/O流量,这将导致其他活动会话性能变差。
-bash-4.2$ vacuumdb --help
vacuumdb cleans and analyzes a PostgreSQL database.
Usage:
vacuumdb [OPTION]... [DBNAME]
Options:
-a, --all vacuum all databases
-d, --dbname=DBNAME database to vacuum
-e, --echo show the commands being sent to the server
-f, --full do full vacuuming
-F, --freeze freeze row transaction information
-j, --jobs=NUM use this many concurrent connections to vacuum
-q, --quiet don't write any messages
-t, --table='TABLE[(COLUMNS)]' vacuum specific table(s) only
-v, --verbose write a lot of output
-V, --version output version information, then exit
-z, --analyze update optimizer statistics
-Z, --analyze-only only update optimizer statistics; no vacuum
--analyze-in-stages only update optimizer statistics, in multiple
stages for faster results; no vacuum
-?, --help show this help, then exit
Connection options:
-h, --host=HOSTNAME database server host or socket directory
-p, --port=PORT database server port
-U, --username=USERNAME user name to connect as
-w, --no-password never prompt for password
-W, --password force password prompt
--maintenance-db=DBNAME alternate maintenance database
Read the description of the SQL command VACUUM for details.
Report bugs to <pgsql-bugs@postgresql.org>.
-bash-4.2$ vacuumdb -d hq -f -z -v >> /tmp/vacuumdb.log
INFO: vacuuming "pg_catalog.pg_statistic"
INFO: "pg_statistic": found 127 removable, 402 nonremovable row versions in 26 pages
1.2. 恢复磁盘空间
在PostgreSQL中,一次行的UPDATE或DELETE不会立即移除该行的旧版本。这种方法对于从多版本并发控制(MVCC)获益是必需的:
当旧版本仍可能对其他事务可见时,它不能被删除。但是最后,任何事务都不会再对一个过时的或者被删除的行版本感兴
趣。它所占用的空间必须被回收来用于新行,这样可避免磁盘空间需求的无限制增长。这通过运行VACUUM完成。
VACUUM的标准形式移除表和索引中的死亡行版本并将该空间标记为可在未来重用。不过,它将不会把该空间交还给操作系统,除非在
特殊的情况中表尾部的一个或多个页面变成完全空闲并且能够很容易地得到一个排他表锁。
相反,VACUUM FULL通过把死亡空间之外的内容写成一个完整的新版本表文件来主动紧缩表。这将最小化表的尺寸,但是要花较长的时间。
它也需要额外的磁盘空间用于表的新副本,直到操作完成。
例行清理的一般目标是多做标准的VACUUM来避免需要VACUUM FULL。
提示: 当一个表因为大量更新或删除活动而包含大量死亡行版本时,纯粹的VACUUM可能不能令人满意。
如果你有这样一个表并且你需要回收它占用的过量磁盘空间,你将需要使用VACUUMFULL,或者CLUSTER,或者ALTER TABLE的表重写变体之一。
提示: 如果你有一个表,它的整个内容会被周期性删除,考虑用TRUNCATE而不是先用DELETE再用VACUUM。
1.3. 更新规划器统计信息
PostgreSQL查询规划器依赖于有关表内容的统计信息来为查询产生好的计划。
这些统计信息由ANALYZE命令收集,它除了直接被调用之外还可以作为VACUUM的一个可选步骤被调用。
拥有适度准确的统计信息很重要,否则差的计划可能降低数据库性能。
自动清理守护进程如果被启用,当一个表的内容被改变得足够多时,它将自动发出ANALYZE命令。
提示: 自动清理守护进程不会为外部表发出ANALYZE命令,因为无法确定一个合适的频度。
1.4. 更新可见性映射
清理机制为每一个表维护着一个可见性映射,它被用来跟踪哪些页面只包含对所有活动事务可见的元组。
这样做有两个目的。
第一,清理本身可以在下一次运行时跳过这样的页面,因为其中没有什么需要被清除。
第二,这允许PostgreSQL回答一些只用索引的查询,而不需要引用底层表。
1.5. 防止事务ID 回卷失败
PostgreSQL的 MVCC 事务语义依赖于能够比较事务 ID(XID)数字
如果一个行版本的插入 XID 大于当前事务的 XID,它就是“属于未来的”并且不应该对当前事务可见。
必要至少每 20 亿个事务就清理每个数据库中的每个表。
周期性的清理能够解决该问题的原因是,VACUUM会把行标记为 冻结,这表示它们是被一个在足够远的过去提交的事务所插入,
这样从 MVCC 的角度来看,效果就是该插入事务对所有当前和未来事务来说当然都 是可见的。
PostgreSQL保留了一个特殊的 XID(FrozenTransactionId),这个 XID 并不遵循普通 XID 的比较规则 并且总是被认为比任
何普通 XID 要老。普通 XID 使用模-232算 法来比较。
VACUUM通常会跳过不含有任何死亡行版本的页面,但是不会跳过那些含有带旧XID值的行版本的页面。
要保证所有旧的行版本都已经被冻结,需要对整个表做一次扫描。vacuum_freeze_table_age控制VACUUM什么时候这样做:如果该表经
过vacuum_freeze_table_age减去vacuum_freeze_min_age个事务还没有被完全扫描过,则会强制一次全表清扫。将这个参数设置为 0
将强制VACUUM总是扫描所有页面而实际上忽略可见性映射。
一个数据库的pg_database行的datfrozenxid列是出现在该数据库中的未冻结 XID 的下界 — 它只是数据库中每一个表的relfrozenxid值的最小值。
SELECT c.oid::regclass as table_name,
greatest(age(c.relfrozenxid),age(t.relfrozenxid)) as age
FROM pg_class c
LEFT JOIN pg_class t ON c.reltoastrelid = t.oid
WHERE c.relkind IN ('r', 'm');
SELECT datname, age(datfrozenxid) FROM pg_database;
age列度量从该截断 XID 到当前事务 XID 的事务数。
VACUUM通常只扫描从上次清理后备修改过的页面,但是只有当全表被扫描时relfrozenxid才能被推进。
当relfrozenxid比vacuum_freeze_table_age个事务还老时、当VACUUM的FREEZE选项被使用时或当所有页面正好要求清理来移除死亡行版本时,全表将被扫描。
如果出于某种原因自动清理无法从一个表中清除旧的 XID,当数据库的最旧 XID 和回卷点之间达到 1 千万个事务时,系统将开始发出这样的警告消息:
WARNING: database "mydb" must be vacuumed within 177009986 transactions
HINT: To avoid a database shutdown, execute a database-wide VACUUM in "mydb".
如果这些警告被忽略,一旦距离回滚点只剩下 1 百万个事务时,该系统将会关闭并且拒绝开始任何新的事务:
ERROR: database is not accepting commands to avoid wraparound data loss in database mydb
HINT: Stop the postmaster and vacuum that database in single-user mode.
由于一旦系统进入到安全关闭模式,它将不会执行命令。做这个操作的唯一方法是停止服务器并且以单一用户启动服务器来执行VACUUM。
1.5.1. 多事务和回卷
Multixact ID被用来支持被多个事务锁定的行。
只要有多于一个事务并发地锁住一个行, 锁信息将使用一个“多个事务ID”(或简称多事务 ID)来编码。
作为一种安全设备,对任何多事务年龄超过 autovacuum_multixact_freeze_max_age的表, 都将发生一次全表清理扫描。
如果已用的成员存储空间超过总量的 50%,全表清理扫描 也将逐步在所有表上进行,这会从那些具有最老多事务年龄的表开始。
1.6. 自动清理后台进程
PostgreSQL有一个可选的但是被高度推荐的特性autovacuum,它的目的是自动执行VACUUM和ANALYZE 命令。
当它被启用时,自动清理会检查被大量插入、更新或删除元组的表。这些检查会利用统计信息收集功能,
因此除非track_counts被设置为true,自动清理不能被使用。在默认配置下,自动清理是被启用的并且相关配置参数已被正确配置。
#autovacuum = on # Enable autovacuum subprocess? 'on'
# requires track_counts to also be on.
#track_counts = on
“自动清理后台进程”实际上由多个进程组成。有一个称为 自动清理启动器的常驻后台进程, 它负责为所有数据库启动自动清理工作者进程。
2. 日常重建索引
在某些情况下值得周期性地使用REINDEX命令或一系列独立重构步骤来重建索引。
已经完全变成空的B树索引页面被收回重用。但是,还是有一种低效的空间利用的可能性:如果一个页面上除少量索引键之外的全部键被删除,该页面仍然被分配。
因此,在这种每个范围中大部分但不是全部键最终被删除的使用模式中,可以看到空间的使用是很差的。对于
这样的使用模式,推荐使用定期重索引。
对于非B树索引可能的膨胀还没有很好地定量分析。在使用非B树索引时定期监控索引的物理尺寸是个好主意。
对于B树索引,一个新建立的索引比更新了多次的索引访问起来要略快, 因为在新建立的索引上,逻辑上相邻的页面通常物理上也相邻
。仅仅为了提高访问速度也值得定期重索引。
REINDEX在所有情况下都可以安全和容易地使用。但是由于该命令要求一个排他表锁,因此更好的方法是用一个由创建和替换步骤组成的序列来执行索引重建。
支持带CONCURRENTLY选项的CREATE INDEX的索引类型可以用这种方式重建。
3. 日志文件维护
把数据库服务器的日志输出保存在一个地方是个好主意, 而不是仅仅通过/dev/null丢弃它们。 在进行问题诊断的时候,日志输出是非常宝贵的。
如果你简单地把postgres的stderr定向到一个文件中,你会得到日志输出, 但是截断该日志文件的唯一方法是停止并重起服务器。这
一个更好的办法是把服务器的stderr输出发送到某种日志轮转程序里。
postgresql.conf里logging_collector为true