环境:
PostgreSQL服务器所在操作系统为:linux
客户端有两个,一个是linux下的:psql
另一个是Windows xp下的:Navicat Premium
发现问题:
在psql中用“create database $数据库名称;”创建数据库时,发现报错:
ERROR: source database "template1" is being accessed by other users
DETAIL: There is 1 other session using the database.
详细见下图:
分析问题:
有错误信息可以看出,模板数据库template1正在被其他用户访问,所以不能在创建数据库时以它为模版进行创建,后台通过“ps -ef|grep postg”,见下图:
从上图可以看出,模板数据库“template1”正在被客户端“192.168.20.104(1666)”访问。这里,需要明白三个问题:
一是,在创建数据库时为什么跟模板数据库template1扯上了关系?
二是,postmaster与postgres进程之间的关系?
三是:“192.168.20.104(1666)”是谁?
先看第一个,原来Postgresql在用initdb创建数据库集群时,将创建一个模板数据库template1,以后所有的数据库都将来源于模板数据库template1,其中也包括模板数据库template0.
换句话说,template1和templat0用于创建数据库,即Postgresql中用从模板数据库复制的方式创建一个新的数据库,而template1就是创建数据库的默认模板。
所以如果用户在创建数据库时不为数据库指定模板,那么将使用template1模板数据库。这便是对问题一的回答,下面来看第二个问题“postmaster与postgres进程之间的关系?”。
原来,postmaster是运行在服务器上的主进程(也称监听守护进程),而postgres是postmaster的子进程,这一点从上图中进行号也可以看出来(27113是postmaster的进程号,而27122是postmaster子进程之一),那么postmaster主进程在什么情况下将开启一个子进程呢?
首先,客户端想访问服务器中某数据库,客户端将调用接口库(如ODBC、libpq等)将用户的请求通过网络发送给主进程postmaster。
接着,当postmaster接收到客户端发送过来的用户请求后,它将启动一个新的后端进程postgres为用户服务,此后,postmaster就将与客户端的交互工作交由postgres服务进程,而有postgres来完成客户端所需要的各种操作。
说完postmaster与postgres进程之间的关系,再来看看“192.168.20.104(1666)”究竟是谁?
在Windows xp的dos窗口中,用“ipconfig”查看到的地址是就是“192.168.20.104”,详见下图:
用Navicat Premium客户端连接Postgresql服务器,连接配置如下图:
看到初始数据库为:template1,一旦用户创建好连接并连接上之后,postmaster主进程就会为客户端192.168.20.104启动一个postgres服务进程(postgres: postgres template1 192.168.20.104(2168) idle)用于与客户端进行交互。
注意:
1.IP地址后括弧中的数字为端口(随机分配一个未用的)
2.初始数据库不一定非要为模板数据库template1,可以任意指定一个已存在于Postgresql服务器中的数据库
解决问题:
通过以上分析,其实要解决这几个问题有以下几种方案:
1.Linux中重启PostgreSQL服务器进程(/etc/rc.d/init.d/postgresql restart)后,再在psql中用“create database $数据库名称;”创建数据库。注:不推荐使用这一方案。
2.关闭Windows xp下的Navicat Premium客户端关于连接到PostgreSQL服务器的连接,然后再在psql中用“create database $数据库名称;”创建数据库。见下图:
3.在linux服务器中用“kill -9 $进程号”杀死PostgreSQL服务进程(本例中的进程号为27122),然后再在psql中用“create database $数据库名称;”创建数据库。
4.在创建数据库时指定模板数据库为template0,指定方式在psql客户端中用“create database $数据库名称 with template=template0;”(中的with可有 可无),在shell命令行(请先切换到postgres用户)中用“createdb $数据库名称 -T template0”或“createdb $数据库名称 --template=template0”均可,“-T或--template”含义见下图: