文章目录
环境
系统平台:银河麒麟 (鲲鹏),Linux x86-64 Red Hat Enterprise Linux 8
版本:9.0.5,4.5.11,4.5.7,4.5.8
症状
1.执行CREATE DATABASE会话长时间挂起无返回,数据库实例不宕机,其他业务SQL可正常执行。
2.部分场景直接抛出报错:
- template1 被占用:source database "template1" is being accessed by other users
其他故障报错:锁超时、磁盘空间不足、目录权限拒绝等。 - 建库会话状态为active,查询该会话pg_stat_activity视图中wait_event_type为Lock;
3.pg_blocking_pids()返回空数组,pg_locks无未授予锁记录,无法通过锁相关函数定位阻塞PID。
问题原因
1.template1模板库存在会话(最常见) CREATE DATABASE默认复制template1模板库;只要template1存在任意连接(idle空闲、idle in transaction、备份/监控工具连接),会直接报错或者会话挂起等待。
该阻塞为数据库上层逻辑校验:建库代码直接检查template1当前连接计数,只要连接数大于0就阻止建库,不会进入底层锁管理器的锁等待队列,锁视图、pg_blocking_pids()均捕获不到阻塞源。
2.长事务、对象锁阻塞:业务长事务持有系统对象锁,造成建库语句锁等待;该场景会话进入锁等待队列,pg_blocking_pids可以识别阻塞pid。
3.存储IO瓶颈:建库需要物理复制模板库文件;磁盘IO打满、await高拖慢复制过程;磁盘/Inode耗尽,无法生成新数据库文件。
4.表空间权限异常:指定表空间建库,表空间目录或上级目录缺少w写入、x进入权限;目录属主不是数据库运行用户;使用NFS/NAS网络存储,存在性能差、锁机制不兼容问题。
5.文件系统异常:目录只读、挂载异常,数据库进程无法新建目录、写入数据文件。
解决方案
操作原则:
1、template1会话导致的报错/挂起,锁相关函数查不到阻塞源,必须单独查询template1会话列表;
2、优先使用template0建库,规避template1锁竞争;
3、生产环境终止会话前务必确认会话业务属性,禁止盲目杀会话。
4、以下排查情况,按照问题出现频率由高到低排序。
一、定位会话与阻塞源
sql
--1.查看卡住的create database会话
highgo=# SELECT pid,usename,datname,state,wait_event_type,wait_event,now()-query_start AS running_time,query
FROM pg_stat_activity
WHERE query ILIKE '%CREATE DATABASE%' AND pid<>pg_backend_pid();
--2.普通锁阻塞查询
--注意:template1连接导致的报错/挂起,此查询blocking_pid为空,属于预期现象
highgo=# SELECT
blocked.pid AS blocked_pid,
blocking.pid AS blocking_pid,
blocking.datname AS blocking_db,
blocking.application_name,
blocking.state,
blocking.query AS blocking_query
FROM pg_stat_activity blocked
LEFT JOIN pg_stat_activity blocking
ON blocking.pid = ANY(pg_blocking_pids(blocked.pid))
WHERE blocked.query ILIKE '%CREATE DATABASE%';
--3.【必执行】排查template1上所有会话
--说明:template1存在会话导致故障,只能靠本条SQL定位,锁视图无法捕获
highgo=# SELECT pid,usename,datname,application_name,client_addr,state,
now()-backend_start AS conn_duration,
now()-xact_start AS xact_duration,
query
FROM pg_stat_activity
WHERE datname = 'template1'
ORDER BY backend_start;
--4.查看所有未授予锁,辅助确认锁等待
--有输出:代表发生底层锁管理器锁等待;
--无输出:说明没有锁排队,CREATE DATABASE故障大概率为template1上层逻辑连接数校验限制。
highgo=# SELECT l.pid,l.locktype,l.database,l.mode,l.granted,
a.usename,a.datname,a.wait_event_type,a.wait_event,a.query
FROM pg_locks l
JOIN pg_stat_activity a ON l.pid = a.pid
WHERE NOT granted;
排查判断逻辑
1.步骤2查询返回blocking_pid不为null:属于普通对象锁阻塞,根据返回结果处理阻塞会话;
2.步骤2查询blocking_pid全部为空:直接看步骤3,如果template1存在会话,即为模板库占用导致故障;
3.步骤4无输出,进一步佐证是template1逻辑层连接数校验,不是锁等待。
| 阻塞场景 | 是否进入锁等待队列 | pg_locks not granted | pg_blocking_pids() | 定位方式 |
|---|---|---|---|---|
| template1存在会话(上层逻辑校验) | ❌不进入锁队列 | 无记录 | 返回空 | 查询pg_stat_activity where datname='template1' |
| 普通对象/事务锁阻塞 | ✅进入锁队列 | 存在记录 | 返回阻塞 | pid pg_blocking_pids + pg_locks |
二、模板库占用导致卡住/报错处理
方案1:直接使用template0模板建库(优先推荐,避开template1锁竞争)
sql
highgo=# CREATE DATABASE testdb
WITH TEMPLATE template0
OWNER sysdba
ENCODING 'UTF8'
LC_COLLATE 'C'
LC_CTYPE 'C';
方案2:清理template1异常会话(生产务必确认会话业务属性)
sql
--终止template1上除当前会话以外所有连接
highgo=# SELECT pg_terminate_backend(pid)
FROM pg_stat_activity WHERE datname='template1' AND pid<>pg_backend_pid();
--确认template1无残留会话
highgo=# SELECT pid,datname,state FROM pg_stat_activity WHERE datname='template1';
--重新执行建库
highgo=# CREATE DATABASE testdb;
方案3:长期预防,禁止普通业务连接template1
sql
highgo=# ALTER DATABASE template1 WITH ALLOW_CONNECTIONS false;
三、处理长空闲事务阻塞
sql
--查看空闲事务会话
highgo=# SELECT pid,usename,datname,state,now()-xact_start xact_age,query
FROM pg_stat_activity WHERE state='idle in transaction' ORDER BY xact_age DESC;
--永久开启空闲事务超时,自动释放,时间单位按需调整
highgo=# ALTER SYSTEM SET idle_in_transaction_session_timeout = '30min';
highgo=# SELECT pg_reload_conf();
highgo=# SHOW idle_in_transaction_session_timeout;
四、存储与IO问题处理
sql
#1.检查磁盘空间、inode
[root@slave ~]# df -h
[root@slave ~]# df -i
#2.观察磁盘IO负载,%util接近100%、await数值高代表IO瓶颈
[root@slave ~]# iostat -x 2 10
1、磁盘空间不足:清理无用日志/过期归档或者扩容磁盘,严禁直接删除pg_wal、base目录文件。
2、IO打满:避开业务高峰执行CREATE DATABASE,业务高峰期不要批量新建数据库。
五、表空间权限异常修复
sql
#1.确认数据库运行用户
[root@slave ~]# ps -ef | grep postgres |grep -v grep
#2.校验表空间目录权限示例,替换为实际表空间路径
[root@slave ~]# su - highgo -c "test -w /data/tbs01 && test -x /data/tbs01 && echo 权限正常 || echo 权限异常"
#3.修复属主和权限
[root@slave ~]# chown highgo:highgo /data/tbs01
[root@slave ~]# chmod 700 /data/tbs01
[root@slave ~]# chmod +x /data
禁止执行chown -R递归修改上层大目录属主;表空间禁止部署NFS/NAS存储,需要迁移本地高性能磁盘。
六、操作后验证
sql
--校验数据库创建完成
highgo=# SELECT datname,pg_encoding_to_char(encoding),pg_size_pretty(pg_database_size('testdb'))
FROM pg_database WHERE datname='testdb';
#连接验证
psql -p 5866 -U sysdba -d testdb -c "select current_database()"
七、生产运维建议
1、监控、备份、脚本统一连接highgo库,不要连接template1。
2、生产新建数据库优先指定TEMPLATE template0。
3、开启idle_in_transaction_session_timeout,防止空闲事务长期持有锁。
4、新建数据库避开业务IO高峰期。
补充知识点:瀚高数据库中pg_backend_pid()返回值即为当前会话PID(会话ID),pid <> pg_backend_pid()作用是排除自身会话,避免误杀当前连接。
sql
--查看当前会话pid
highgo=# select pg_backend_pid();