自省 自行 自醒

懂得正确地提出问题,也是一项重要的本领

Word count: 1.1kReading time: 4 min
2025/02/27
loading

前言

这几天,一位读者咨询了我这样一个问题:

如果有自定义的表空间,那么如何用 pg_upgrade 进行大版本升级呢,我自己测试了,发现会报错,那如果 pg_upgrade 不行的话,那还有什么工具升级大版本呢。还是说只能够通过数据的逻辑导出导入的方式进行升级

愣是一下子没有反应过来,印象中 pg_upgrade 会妥善处理好表空间。当分析清楚问题根因之后,再次感叹,懂得正确地提出问题,也是一项重要的本领

分析

首先,大版本升级并不仅限于 pg_upgrade 和逻辑导入导出,还有逻辑复制,三者各有优劣,此处表过不提。

回到 pg_upgrade,印象中是可以正确处理表空间的,包括在官方文档中也并没有提及有相关限制。于是要了一个详细的错误截图:

可以看到,提示 “Cannot upgrade to/from the same system catalog version when using tablespaces”,代码很好找,位于 pg_upgrade.c 中:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
void
check_and_dump_old_cluster(bool live_check)
{
/* -- OLD -- */

if (!live_check)
start_postmaster(&old_cluster, true);

/*
* Extract a list of databases, tables, and logical replication slots from
* the old cluster.
*/
get_db_rel_and_slot_infos(&old_cluster, live_check);

init_tablespaces();

get_loadable_libraries();
...


void
init_tablespaces(void)
{
get_tablespace_paths();

set_tablespace_directory_suffix(&old_cluster);
set_tablespace_directory_suffix(&new_cluster);

if (os_info.num_old_tablespaces > 0 &&
strcmp(old_cluster.tablespace_suffix, new_cluster.tablespace_suffix) == 0)
pg_fatal("Cannot upgrade to/from the same system catalog version when\n"
"using tablespaces.");
}

static void
set_tablespace_directory_suffix(ClusterInfo *cluster)
{
/* This cluster has a version-specific subdirectory */

/* The leading slash is needed to start a new directory. */
cluster->tablespace_suffix = psprintf("/PG_%s_%d",
cluster->major_version_str,
cluster->controldata.cat_ver);
}

代码逻辑很简单,就是比较新老集群的表空间的后缀名字

  • cluster->major_version_str:PostgreSQL 的主版本号(比如 “14” 或 “15” 等)
  • cluster->controldata.cat_ver:PostgreSQL 集群的 Catalog version number

到这里,不得不提一下表空间的命名方式了:PG + 主版本号 + CATALOG_VERSION_NO:

1
2
3
4
5
/*
* Name of major-version-specific tablespace subdirectories
*/
#define TABLESPACE_VERSION_DIRECTORY "PG_" PG_MAJORVERSION "_" \
CppAsString2(CATALOG_VERSION_NO)

CATALOG_VERSION_NO 可以使用 SQL 或者 pg_controldata 进行查询,主要目的是防止针对格式不兼容的数据目录进行启动:

1
2
3
4
5
postgres=# select * from pg_control_system();
pg_control_version | catalog_version_no | system_identifier | pg_control_last_modified
--------------------+--------------------+---------------------+--------------------------
1300 | 202404021 | 7456358899425219394 | 2025-02-24 10:16:58+08
(1 row)

所以按照这个命名方式,假如我新建一个表空间,那么命名将是 PG_17_202404021:

1
2
3
4
5
postgres=# create tablespace myspace location '/home/postgres/myspace';
CREATE TABLESPACE
[postgres@mypg ~]$ ls -l 17data/pg_tblspc/16702/
total 4
drwx------ 2 postgres postgres 4096 Feb 27 16:57 PG_17_202404021

那么回到问题中来,如果是不同大版本,主版本号就不同了,不应该报错,为何会报错呢?就在我百思不得其解之时,我又问了一下这位读者更多信息:

我这边是测试 14.7 升级到 14.17

我是看到这个 pg_upgrade 小版本升级报错了,就以为大版本也会有问题

🙃🙃🙃好吧,原来是一场乌龙,只是因为上下文不够清晰,小版本只需替换二进制,然后重启就行了。

小结

懂得正确地提出问题,也是一项重要的本领。作者”人神共奋的李刚”就说:

职场中,70% 的时间花在各种沟通 (听说读写) 上,真正完成任务的时间不足 30%,造成群体工作绩效低的第一大原因就是”沟通不良”,而沟通不良的第一大杀手就是不会问问题。

精确地描述问题并言之有物是十分重要的,以《提问的智慧》为例:

  • 仔细、清楚地描述你的问题或 Bug 的症状。
  • 描述问题发生的环境(机器配置、操作系统、应用程序、以及相关的信息),提供经销商的发行版和版本号 (如:Fedora Core 4、Slackware 9.1等)。
  • 描述在提问前你是怎样去研究和理解这个问题的。
  • 描述在提问前为确定问题而采取的诊断步骤。
  • 描述最近做过什么可能相关的硬件或软件变更。
  • 尽可能地提供一个可以重现这个问题的可控环境的方法。

这个案例,如果事先说明是小版本升级,那么我就不用任何思考即可作出回答,也不需要花费时间去看代码,毕竟大家的时间都非常宝贵,回答问题也不是义务。最后,推荐各位都阅读一下《提问的智慧》

参考

https://github.com/ryanhanwu/How-To-Ask-Questions-The-Smart-Way/blob/main/README-zh_CN.md

CATALOG
  1. 1. 前言
  2. 2. 分析
  3. 3. 小结
  4. 4. 参考