PostgreSQL的几种常见问题和解决方法
1. 前言
1.1 概述
本文介绍了postgresql的几种常见问题,并从现象出发,逐步排查问题,分析导致问题的原因并给出解决方案。
本文介绍的问题分为两大类:一类是关于PostgreSQL无法启动的问题,另一类是PostgreSQL启动后,部分数据库对象无法访问的问题。
1.2 软件环境
本文使用的 PostgreSQL 版本是 9.2。
1.3 一些约定术语
PostgreSQL安装路径:表示IVMS-8700平台PostgreSQL的安装路径,默认是 “D:\Program Files\PostgreSQL\9.6”
bin 文件夹:PostgreSQL安装路径下的bin文件夹。
data 文件夹:PostgreSQL安装路径下的data文件夹。
2. 问题和解决方法
2.1 PostgreSQL无法启动
PostgreSQL 没有正常启动时,在 “服务”中再次启动失败。
2.1.1 端口占用
我们首先需要判断是不是该服务的端口被占用。PostgreSQL服务的默认端口是5432,那么我们在命令行中执行如下命令
netstat -ano | find /i "5432"
如果发现了某个进程使用了5432这个端口,这说明是端口占用导致服务无法启动:
这个进程的pid是2364,你想查看它是什么进程,可以执行:
tasklist | findstr "2364"
执行结果如下:
你可以在任务管理器-进程页面中,或者通过下面的命令结束这个进程:
taskkill /f /pid 5432
2.1.2 could not open control file “global/pg_control”:Permission denied
如果端口没有被占用,那么你可以用PostgreSQL原生的命令启动它。
进入postgresql安装路径下的 bin 文件夹,在这里打开命令行,执行下面的命令:
.\pg_ctl start -D ..\data
如果程序报出如下错误:
ERROR: could not open control file “global/pg_control”: Permission denied
则说明当前操作系统用户丢失了data文件夹及其内容的权限。
下面是解决方法:
1. 首先,进入postgresql 的安装路径,右键data文件夹,依次点击属性——安全——编辑,你能看到所有用户或用户组的权限。
2. 确保System 和 Administrator 拥有“完全控制”权限。Users 用户组默认只拥有“读取和执行”,“列出文件夹内容”和“读取”3种权限。当启动数据库提示“权限不足”时,应再添加“修改”和 “写入”。
3. 保存并尝试再次在bin 文件夹下执行:
.\pg_ctl start -D ..\data
观察PostgreSQL数据库能否启动。
2.1.3 could not locate a valid checkpoint record
如果启动数据库时,提示“正在启动服务器进程”,且长时间无法启动成功,如下图所示,需要查看数据库运行日志,它们位于data文件夹下的pg_log中的。
打开问题发生时的数据库运行日志,查看信息。
如果日志中出现类似下面黑体字的信息,说明是PostgreSQL数据库中的预写式日志(write ahead log,简称WAL,又称事务日志,简称xlog)损坏了:
LOG: could not open file "pg_xlog/0000000100000000000000E7" (log file 0, segment 231): No such file or directory
LOG: invalid primary checkpoint record
LOG: could not open file "pg_xlog/0000000100000000000000E7" (log file 0, segment 231): No such file or directory
LOG: invalid secondary checkpoint record
PANIC: could not locate a valid checkpoint record
解决方法如下:
进入bin 文件夹,在这里打开命令行,执行下面的命令:
.\pg_resetxlog.exe -f ..\data
在日志重置后,再尝试启动数据库。
2.1.4 无法找到来自源 PostgreSQL 的事件 ID 0 的描述。
如果上面的方法没有解决问题,那么我们需要进入事件管理器中查看是否有错误日志:
在事件查看器-Windows日志-应用程序中,查看是否有如下错误日志:
无法找到来自源 PostgreSQL 的事件 ID 0 的描述。本地计算机上未安装引发此事件的组件,或者安装已损坏。可以安装或修复本地计算机上的组件。
如果出现了这样的信息,则说明PostgreSQL软件已经损坏,需要重新安装。不过,数据文件不一定损坏了,因此如果上次备份至今,数据库中产生过非常重要的数据(比如账单信息),你应该将data文件夹复制到另一个目录,然后重新安装平台,并恢复data文件夹。
2.1.5 Could not read from file "pg_clog/000E" at offset 172032
还有一种不常见的情况。如果日志中出现类似下面的信息:
ERROR: could not access status of transaction 710708
DETAIL: Could not read from file "pg_clog/000E" at offset 172032: No error.
则表示位于data文件夹下pg_clog中的名为 000E 的提交日志文件丢失了。
解决方法如下:
在linux 操作系统中,执行下列命令:
dd if=/dev/zero of=/root/000E bs=256k count=1
或者在windows中安装 dd,随后执行:
dd if=/dev/zero of=D:\000E bs=256k count=1
然后将创建好的000E 文件拷贝至data文件夹下的pg_clog 中。
2.2 数据库启动后,部分数据库或表无法访问
这种情况下,你需要进入 data文件夹下的pg_log文件夹,查看问题发生时刻产生的运行日志。
2.2.1 permission denied for relation tb_door
如果运行日志出现类似下面的信息,这说明是当前访问用户没有表tb_door的某些权限:
ERROR: permission denied for relation tb_door
如果你希望当前用户(以my_user为例)拥有特定访问权限(以SELECT,INSERT,UPDATE ,DELETE为例),可以这样解决:
- 首先,通过postgres用户或拥有tb_door 相应访问权限即授予权限的用户登录数据库;
- 执行如下命令,为用户授予权限:grant SELECT,INSERT,UPDATE,DELETE on tb_door to my_user
2.2.2 invalid page header in block 120 of relation base/272816/309624
如果日志中出现类似下面的信息:
ERROR: invalid page header in block 120 of relation base/272816/309624
则表示数据表文件损坏。这通常是由于异常断电或误操作导致的。这里“272816”是发生问题的数据库的对象id(oid), “309624”表示发生问题的表的文件结点(filenode)
如果发生损坏的表以及损坏的页面数量较少,我们可以以牺牲部分数据的代价恢复整体;如果损坏的表数量过多,或者损失的数据非常重要,就需要从备份中恢复数据了。
当发生损坏的表以及损坏的页面数量较少时,解决方法如下:
- 确定发生问题的数据库。连接任意数据库,执行下面的sql语句:
select datname from pg_database where oid = 272816;
查询结果如下:
testdb
这表示发生问题的数据库名是testdb
2. 查找损坏的数据库对象。连接发生问题的数据库,执行下面的sql语句:
select relname,relkind from pg_class where relfilenode = 309624
如果查询结果中 relkind = r,表示损坏的是表。
例如:
tb_door, r
relname = tb_door这表示损坏的表是tb_door。
如果查询结果中relkind = i,表示损坏的是一个索引。
例如:
dept_number_index, i
或者:
tb_dept_pkey, i
需要注意,损坏的可能是普通索引,也可能是主键或唯一键。如果索引的名称中有“_pkey”等很可能属于主键,而名称中含有 “_key”则很可能属于唯一键。
还需要格外注意一点,表/索引可修复的前提条件是损坏的表是应用程序创建的表/索引,而不是PostgreSQL的系统表和建立在其上的索引。如果系统表/建立在其上的索引发生损坏,则需要从备份中恢复数据库。判断一个表是否是系统表,最简单的方法是:如果表名是“pg_”开头的,则说明它是系统表。
小知识 pgclass.relkind 的值有下面几种: r: 表示ordinary table(普通表); i: 表示index(索引); S: 表示sequence(序列); V: 表示view(视图); m: 表示materialized view(物化视图); c: 表示composite type(复合类型); t: 表示TOAST table(TOAST 表); f: 表示foreign table(外部表) |
3. 修复损坏的数据库对象。连接发生损坏的数据库,执行修复命令。
如果损坏的是表,以tb_door为例,则依次执行下列命令即可完成修复:
set zero_damaged_pages = on;
vacuum full tb_door;
reindex table tb_door;
如果损坏的是普通索引,以dept_number_index为例, 则依次执行:
set zero_damaged_pages = on;
reindex index dept_number_index;
如果损坏的是主键或唯一键,则首先需要找到它所在的表,以tb_dept_pkey为例:
Select tablename,indexname from pg_indexes where indexname = ‘tb_dept_pkey’;
查询结果:
tb_dept, tb_dept_pkey
然后获取索引的定义:
select pg_get_constraintdef((select oid from pg_constraint where conname = ' tb_dept_pkey '));
查询结果:
PRIMARY KEY (dept_id)
然后重新创建这个约束:
Alter table drop constriant tb_dept_pkey;
Alter table add constraint tb_dept_pkey PRIMARY KEY (dept_id);
2.2.3 could not read block 190 in file "base/272816/309624"
该问题的解决方法与2.2.2 节的问题完全相同。
2.2.4 could not open file "base/272816/379923": No such file or directory
如果日志中出现类似下面的信息:
2019-01-21 14:28:03 HKT ERROR: could not open file "base/272816/379923": No such file or directory
则说明,oid为272816的数据库中,oid为379923的表对应的文件被删除了。
解决方法如下:
1. 首先判断是哪一个数据库中发生了此问题。连接任意数据库,执行如下sql:
select datname from pg_database where oid = 272816
查询结果如下:
testdb
2. 从备份中恢复该数据库。