暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

逐步弄清楚PostgreSQL中的unix_socket_directories参数

数据库杂记 2023-07-08
846


前言

有关unix_socket_directories这个参数的使用,经常碰到一些同学问起。原因在于里边有几处在文档中说的有些模糊不清的地方。看来有必要来一次小结。

本来它的使用,没有太多的重大意义。毕竟生产环境中,靠的还是socket的listen_address和port去服务于远程连接为主。而unix_socket_directories主要用于本机的连接。对于想往深里挖一下,了解这个参数的具体用法也无可厚非。

本文就以CentOS7.9下边的PostgreSQL 14.8为例 ,看看实际情况是什么样的?

分析与实作

1 采用默认参数

这种情况下,在postgresql.conf中我们什么也不设,看到的是下边的这种情况:

1#unix_socket_directories = '/var/run/postgresql, /tmp'  # comma-separated list of directories
2                                        # (change requires restart)
3#unix_socket_group = ''                 # (change requires restart)
4#unix_socket_permissions = 0777         # begin with 0 to use octal notation
5                                        # (change requires restart)

这说明PG起来的时候,默认支持这两个目录作为:unix_socket_directories。它是安装版自带的默认值。并且/var/run/postgresql目录是第一个值。psql作为客户端去连接的时候,默认会采用这第一个值去连。

我们启动一个默认的PG试一下:

 1[06:21:18-postgres@sean-rh1:/var/lib/pgsql/data3]$ pg_ctl start -D ./
2waiting for server to start....2023-07-07 06:21:23.264 UTC [5756LOG:  redirecting log output to logging collector process
32023-07-07 06:21:23.264 UTC [5756] HINT:  Future log output will appear in directory "log".
4 done
5server started
6[06:21:23-postgres@sean-rh1:/var/lib/pgsql/data3]$ psql
7psql (14.8)
8Type "help" for help.
9
10postgres=#

 1[06:21:40-postgres@sean-rh1:/var/lib/pgsql/data3]$ psql -h /tmp
2psql (14.8)
3Type "help" for help.
4
5postgres=# \q
6[06:21:45-postgres@sean-rh1:/var/lib/pgsql/data3]$ psql -h /var/run/postgresql
7psql (14.8)
8Type "help" for help.
9
10postgres=# \conninfo
11You are connected to database "postgres" as user "postgres" via socket in "/var/run/postgresql" at port "5555".
12postgres=#

当psql不带-h参数时,并且没有指定环境变量PGHOST时,它会默认采用 -h {1st unix_socket_directories}。即采用的是unix_socket_directories的第一个目录用作所谓的"host",  连接这个目录下边的socket文件。

2 采用定制的参数

这时,有朋友来问题了,我就不想用默认的这两个参数,自己来个新的。比如: "/tmp2",这个时候,别忘了改下访问权限,不然,可能就起不来了。

看看参数:unix_socket_permissions,  它就是起这个作用的。默认值需要设置为0777。

The default permissions are 0777
, meaning anyone can connect. Reasonable alternatives are 0770
(only user and group, see also unix_socket_group
) and 0700
(only user). (Note that for a Unix-domain socket, only write permission matters, so there is no point in setting or revoking read or execute permissions.)

 1unix_socket_directories = '/tmp2' 
2
3建目录:
4[07:04:13-postgres@sean-rh1:/var/lib/pgsql/data3]$ sudo mkdir /tmp2
5[07:04:18-postgres@sean-rh1:/var/lib/pgsql/data3]$ ls -la /tmp2
6total 8
7drwxr-xr-x   2 root root 4096 Jul  7 07:04 .
8dr-xr-xr-x. 20 root root 4096 Jul  7 07:04 ..
9
10启PG: (失败了)
11[07:06:15-postgres@sean-rh1:/var/lib/pgsql/data3]$ pg_ctl start -D ./
12waiting for server to start....2023-07-07 07:06:21.266 UTC [5925LOG:  redirecting log output to logging collector process
132023-07-07 07:06:21.266 UTC [5925] HINT:  Future log output will appear in directory "log".
14 stopped waiting
15pg_ctl: could not start server
16Examine the log output.

我们看下log,有如下错误显示:

12023-07-07 07:06:21.266 UTC [5925LOG:  starting PostgreSQL 14.8 on x86_64-pc-linux-gnu, compiled by gcc (GCC) 4.8.5 20150623 (Red Hat 4.8.5-44), 64-bit
22023-07-07 07:06:21.267 UTC [5925LOG:  listening on IPv6 address "::1", port 5555
32023-07-07 07:06:21.267 UTC [5925LOG:  listening on IPv4 address "127.0.0.1", port 5555
42023-07-07 07:06:21.268 UTC [5925] FATAL:  could not create lock file "/tmp2/.s.PGSQL.5555.lock": Permission denied
52023-07-07 07:06:21.270 UTC [5925LOG:  database system is shut down

原因也很简单,因为上边看到了:/tmp2的权限为:drwxr-xr-x  (for root用户),差不多也就是755。改下权限就可以了。

1[07:07:55-postgres@sean-rh1:/var/lib/pgsql/data3]$ sudo chmod 0777 /tmp2
2[07:08:03-postgres@sean-rh1:/var/lib/pgsql/data3]$ pg_ctl start -D ./
3waiting for server to start....2023-07-07 07:08:09.569 UTC [5935LOG:  redirecting log output to logging collector process
42023-07-07 07:08:09.569 UTC [5935] HINT:  Future log output will appear in directory "log".
5 done
6server started

好,到了这一步,我们要试试psql去连接看下:

1[07:08:39-postgres@sean-rh1:/var/lib/pgsql/data3]$ psql
2psql: error: connection to server on socket "/var/run/postgresql/.s.PGSQL.5555" failed: No such file or directory
3        Is the server running locally and accepting connections on that socket?

不指定具体的socket目录,它会一直报这样的错。你可能百思不得其解,为何一直是报的这个目录:"/var/run/postgresql"???

怎么不是"/tmp"或其它呢? 这个问题放到最后。它应该是PostgreSQL12以及以后版本默认的行为。

为了让其能工作,我们带上参数: -h tmp2

1[07:11:02-postgres@sean-rh1:/var/lib/pgsql/data3]$ psql -h /tmp2 -c "select version()"
2                                                 version
3---------------------------------------------------------------------------------------------------------
4 PostgreSQL 14.8 on x86_64-pc-linux-gnu, compiled by gcc (GCC) 4.8.5 20150623 (Red Hat 4.8.5-44), 64-bit
5(1 row)

这样能很顺利的连接上数据库了。

当然,还可以用下边的两种办法,少输入这个参数:

2.1 使用环境变量PGHOST

1[07:11:08-postgres@sean-rh1:/var/lib/pgsql/data3]$ export PGHOST=/tmp2
2[07:13:04-postgres@sean-rh1:/var/lib/pgsql/data3]$ psql -c "select 1"
3 ?column?
4----------
5        1
6(1 row)

2.2 使用连接服务文件

连接服务文件专用于libpq客户端,用于建立到server端的连接。别的类别的客户端比如pg-jdbc,那是用不上的。

详细情况,可以读下文档:https://www.postgresql.org/docs/14/libpq-pgservice.html

默认情况下,linux上可以读取文件:~/.pg_service.conf,windows上可以读取%APPDATA%\postgresql.pg_service.conf。

定制的文件名可以通过环境变量:PGSERVICEFILE来决定。默认用户范围的文件名是:.pg_service.conf。系统范围的名字是:pg_service.conf。环境变量PGSERVICEFILE指定的应该是文件的全路径。两者都有的话,用户范围优先。如果文件名不变,只是变目录位置,可以读取:PGSYSCONFDIR。

同时有一个全局的sysconfdir位置,它的值可以通过如下命令得到:

1[07:19:56-postgres@sean-rh1:/var/lib/pgsql/data3]$ pg_config --sysconfdir
2/etc/sysconfig/pgsql

你可以在这个全局目录下边,建一个service文件:pg_service.conf,这样,所有用户都可以使用它。

一个标准的样例服务文件为:(~/.pg_service.conf)

1# comment
2[mydb]
3host=/tmp2
4port=5555
5user=postgres

好,我们不多说,建一个上边的文件到postgres的home目录下边。

1cat ~/.pg_service.conf
2
3[mydb]
4host=/tmp2
5port=5555
6user=postgres

这里稍解释一下,[mydb]是注册的连接服务名,到时候要通过它来找到对应的注册项。

我们再使用这神奇的方法,又分两种参数方式:

1 直接用环境变量PGSERVICE

注意这里头的环境变量都是用于postgres的客户端,千万别弄混了。看看效果:

1[07:32:54-postgres@sean-rh1:/var/lib/pgsql/data3]$ PGSERVICE=mydb2 psql
2psql: error: definition of service "mydb2" not found
3[07:33:00-postgres@sean-rh1:/var/lib/pgsql/data3]$ PGSERVICE=mydb psql -c "select 1"
4 ?column?
5----------
6        1
7(1 row)

我们还可以简单验证下不同的文件名的效果:

 1vi mypgservice.conf
2[foo]
3host=/tmp2
4port=5555
5user=postgres
6
7[07:34:44-postgres@sean-rh1:/var/lib/pgsql/data3]$ PGSERVICE=foo PGSERVICEFILE=/var/lib/pgsql/data3/mypgservice.conf psql -c "SELECT 1"
8 ?column?
9----------
10        1
11(1 row)

2 利用libpq中的连接参数 service

直接利用参数"service=mydb"为定位连接服务的相关参数host值。接着上例,我们很容易验证:

1[07:34:52-postgres@sean-rh1:/var/lib/pgsql/data3]$ psql service=mydb -c "select 1"
2 ?column?
3----------
4        1
5(1 row)

加上服务文件定制化,一样可以用下边的命令:

1[07:36:12-postgres@sean-rh1:/var/lib/pgsql/data3]$ PGSERVICEFILE=/var/lib/pgsql/data3/mypgservice.conf psql service=foo -c "SELECT 1"
2 ?column?
3----------
4        1
5(1 row)

这里用的是service=foo, 因为我们在/var/lib/pgsql/data3/mypgservice.conf注册的服务名是"foo"。

3 为何老是报这个位置的错:/var/run/postgresql?

前边两小节介绍的是基本的用法及相关的实用小技巧。基本上你读到了文档,动手试了试,就能准确理解。那么,在安装版的psql连接时,我们如果指定了不同的unix_socket_directories值,默认情况下,它总是报类似于下边的错

1psql: error: connection to server on socket "/var/run/postgresql/.s.PGSQL.5555" failed: No such file or directory
2--这里的5555到了你那里可能就是你设置的默认端口的值

为什么?可以多问几下。

这个倒是跟Server端没啥关系,反倒是跟psql这个客户端程序的实现有关系。我们还是以PostgreSQL14 为例 ,先看看源代码:

文件:postgres\src\interfaces\libpq\fe-connect.c

 1static void
2emitHostIdentityInfo(PGconn *conn, const char *host_addr)
3
{
4#ifdef HAVE_UNIX_SOCKETS
5    if (IS_AF_UNIX(conn->raddr.addr.ss_family))
6    {
7        char        service[NI_MAXHOST];
8
9        pg_getnameinfo_all(&conn->raddr.addr, conn->raddr.salen,
10                           NULL0,
11                           service, sizeof(service),
12                           NI_NUMERICSERV);
13        appendPQExpBuffer(&conn->errorMessage,
14                          libpq_gettext("connection to server on socket \"%s\" failed: "),
15                          service);
16    }

是在这一块儿。service的值就决定了最终用的是什么。

也就是说Server端在postgresql.conf里头设置的unix_socket_directories的值(默认是:/var/run/postgresql, tmp)与psql客户端程序代码中的默认值极有可能不一致。

两者一样,都用默认值时,psql的-h用的始终是第一个目录:/var/run/postgresql,除非你指定一个不同的值。

这个验证起来也直接,一种就是直接debug。另一种就是反向修改代码,编译生成你想要的psql可执行代码。我们还可以从psql的文本串中反推这样的结论。使用安装版的psql,可以得到:

1[07:56:23-postgres@sean-rh1:/var/lib/pgsql/data3]$ strings -a /usr/pgsql-14/bin/psql | grep "/tmp"
2/tmp
3[07:56:31-postgres@sean-rh1:/var/lib/pgsql/data3]$ strings -a /usr/pgsql-14/bin/psql | grep "/var/run/postgresql"
4/var/run/postgresql
5
6[07:56:55-postgres@sean-rh1:/var/lib/pgsql/data3]$ strings -a /usr/pgsql-14/lib/libpq.so | grep "connection to server on socket "
7connection to server on socket "%s" failed:

从这上边,我们看到什么了吗?看到了,它包含:/tmp,  也包含了:/var/run/postgresql

我们再看看我们自己默认编译项:

1[07:57:15-postgres@sean-rh1:/var/lib/pgsql/data3]$ /usr/pgsql-14-build/bin/pg_config --configure
2 '--with-openssl' '--prefix=/usr/pgsql-14-build' '--enable-debug'

它编译出来的psql相关的目录值:

1[07:58:44-postgres@sean-rh1:/var/lib/pgsql/data3]$ strings -a /usr/pgsql-14-build/bin/psql | grep "/tmp"
2/tmp
3[07:59:27-postgres@sean-rh1:/var/lib/pgsql/data3]$ strings -a /usr/pgsql-14-build/bin/psql | grep "/var/run/postgresql"
4[07:59:36-postgres@sean-rh1:/var/lib/pgsql/data3]$

发现没有,它压根就没有:/var/run/postgresql。

这时如果你使用/usr/pgsql-14-build/bin/psql去连接/tmp这个socket,不带参数就可以。因为它的默认socket目录就是"/tmp"

我们来个简单验证:

1unix_socket_directories = '/tmp'
2
3重启postgres
4[08:01:57-postgres@sean-rh1:/var/lib/pgsql/data3]$ ppg_ctl start -D ./

用新的编译的psql去连接:

1[08:02:45-postgres@sean-rh1:/var/lib/pgsql/data3]$ unset PGSERVICE PGSERVICEFILE
2[08:03:01-postgres@sean-rh1:/var/lib/pgsql/data3]$ /usr/pgsql-14-build/bin/psql
3psql (14.4, server 14.8)
4Type "help" for help.
5
6postgres=#

用安装版的psql去连接:

1[08:04:16-postgres@sean-rh1:/var/lib/pgsql/data3]$ /usr/pgsql-14/bin/psql
2psql: error: connection to server on socket "/var/run/postgresql/.s.PGSQL.5555" failed: No such file or directory
3        Is the server running locally and accepting connections on that socket?

这就是中间的区别。

4 再好奇一把

我们直接修改源码,我们直接搜索:DEFAULT_PGSOCKET_DIR,  定位到文件:postgres\src\include\pg_config_manual.h,

1#ifndef WIN32
2#define DEFAULT_PGSOCKET_DIR  "/tmp"
3#else
4#define DEFAULT_PGSOCKET_DIR ""
5#endif

把这个默认的"/tmp", 你改一下,改成:"/var/run/postgres",再编译安装psql客户端的那块。

1wget https://ftp.postgresql.org/pub/source/v14.8/postgresql-14.8.tar.gz
2tar xf postgresql-14.8.tar.gz
3cd postgresql-14.8
4
5vi src/include/pg_config_manual.h
6#define DEFAULT_PGSOCKET_DIR  "/var/run/postgresql"
7
8./configure --with-openssl --prefix=/usr/pgsql-14-build2 --enable-debug

好,我们这里只编译client端。

1sudo make -j4 -C src/bin install
2sudo make -j4 -C src/include install
3sudo make -j4 -C src/interfaces install

安装完以后,我们再针对前边的"/tmp"的PG server做做验证:

4.1 socket目录为"/tmp"

1[08:32:58-postgres@sean-rh1:/var/lib/pgsql/data3]$ /usr/pgsql-14-build2/bin/psql
2psql: error: connection to server on socket "/var/run/postgresql/.s.PGSQL.5555" failed: No such file or directory
3        Is the server running locally and accepting connections on that socket?

如此一来,我们就重现了安装版本中出现的这个报错。

无论什么样的配置,它都是以默认的/var/run/postgresql目录来建立unix socket连接的。除非显式指定:-h tmp

1[08:33:21-postgres@sean-rh1:/var/lib/pgsql/data3]$ /usr/pgsql-14-build2/bin/psql -h /tmp -c "select 1"
2 ?column?
3----------
4        1
5(1 row)

4.2 socket目录为“/tmp, var/run/postgresql"

1vi postgresql.conf
2unix_socket_directories = '/tmp, /var/run/postgresql'
3
4重启postgres

连接:

1[08:37:45-postgres@sean-rh1:/var/lib/pgsql/data3]$  /usr/pgsql-14-build2/bin/psql -c "select 1"
2 ?column?
3----------
4        1
5(1 row)

1[08:37:51-postgres@sean-rh1:/var/lib/pgsql/data3]$ /usr/pgsql-14-build2/bin/psql -h /var/run/postgresql -c "select 1"
2 ?column?
3----------
4        1
5(1 row)

读到这里,可能大家也就清楚了,网上的安装版(没有记错的话,应该是从PostgreSQL12.x开始,从strings -a {psql}可以反推得到),是修改了文件:postgres\src\include\pg_config_manual.h,

把默认值:DEFAULT_PGSOCKET_DIR 从"/tmp"改成了:"/var/run/postgresql"

小结:

这篇短文写的其实挺啰嗦的。只为了把一件事情给介绍明白。仍然感谢一些朋友私下留言,鼓励继续分享下去。内容有的虽然略显简单,但是可以建立起Architect、DBA与Devloper之间的桥梁。有的时候,发现他们之间的GAP其实还是蛮大的,有的过重于去分析一个出了严重故障后的数据库,那个就相当于在数据库得了重病需要救治的状态。这个当然能突显个人的能力。

有很多时候,还是开发人员对DB不怎么熟悉,有的东西该避免没有避免,以至于最后引起问题。而对于架构方,更应该注意目标数据库哪些地方是优势,要充分发挥,哪些地方是劣势,更应该避免。该 压榨和利用的地方,充分利用,该避免的地方,也需要备加留意。三方合作才能真正共赢。


文章转载自数据库杂记,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论