坑边闲话:最近正值双十一,关于淘宝超高并发的数据库大家可能都有所耳闻。数据库作为现代软件工业中最重要的技术,其蕴含的技术难度可谓是相当大。从关系型数据库的数学原理,到各种高级的数据结构,再到复杂的缓存优化,它们无不向 IT 从业者时刻展示这项技术的光辉与荣耀。本文创作于笔者给本地数据库进行升级、迁移的过程中,这或许并不是一个最佳实践,但我相信这篇文章肯定可以告诉你许多不为人知的数据库秘密。

图 . 笔者的灵感来源之一,某 X 平台博主描述的数据库升级过程。

1. 常见数据库·

1.1 PostgreSQL·

PostgreSQL 是一个开源、稳定且功能极其强大的关系型数据库,被誉为「世界上最先进的开源数据库」。它支持标准 SQL,同时提供丰富的扩展能力,例如 JSON、地理空间数据、全文检索和用户自定义函数。许多企业级产品,包括 GitLab、Mattermost、维基媒体后台,都在使用 PostgreSQL 作为核心数据库。它在可靠性、一致性和复杂查询优化方面表现突出,非常适合中大型业务系统以及对数据完整性要求极高的场景。

PostgreSQL 每年发布一个主版本(如 15、16、17),节奏固定且透明。每个主版本在发布后会提供 至少 5 年的官方维护支持,期间会持续提供安全补丁和稳定性修复,但不会引入破坏性变更。小版本更新(如 16.1 → 16.2)通常每季度发布一次,可直接升级且无需停库迁移。主版本升级需要 pg_upgrade 或数据迁移工具。这样的更新策略兼顾稳定性与创新,使 PostgreSQL 在企业级数据库中长期可靠。笔者建议在大多数情况下选择使用 PostgreSQL 即可。

图 . PostgreSQL 的生命周期。

很多人会疑惑:应用程序是否必须与 PostgreSQL 的版本严格匹配?例如,原本部署在 PostgreSQL 14 上的程序,是否可以直接换成 PostgreSQL 18 而不改应用代码?

在大多数情况下,答案是可以

应用程序一般并不依赖 PostgreSQL 的内部实现,而是通过标准的 PostgreSQL 通信协议 进行交互(如 libpqJDBC、Go 的 pgx 等驱动)。只要:

  • 程序使用的 SQL 语法和特性没有在新版中删除或变更,
  • 驱动版本支持所连接的 PostgreSQL 版本,

那么应用和数据库就不需要一一对应的版本绑定。也就是说,应用程序关心的不是 PostgreSQL 的版本号,而是 驱动和 SQL 功能是否兼容。

1.2 MariaDB·

MariaDB 是 MySQL 的一个社区驱动分支,由 MySQL 原始作者在 Oracle 收购 MySQL 后创建。它完全开源,遵循 GPL 许可证,在性能、稳定性和扩展性方面持续增强。MariaDB 保持与 MySQL 的高兼容性,绝大多数情况下可以直接替换使用,同时提供更多存储引擎(如 MyRocks、ColumnStore)以支持高并发和数据分析场景。凭借开放透明的社区治理模式和广泛的生态支持,MariaDB 已成为企业和自托管环境中常见的关系型数据库选择。

MariaDB 的更新通常遵循 半年一次主版本发布 的节奏(例如 10.6、10.7、10.11),新版本会带来性能优化、新存储引擎和兼容性改进。与 PostgreSQL 采用统一 5 年支持不同,MariaDB 的支持周期与版本性质有关:LTS(长期支持版本)提供约 5 年维护,普通版本维护约 1 年。小版本更新主要修复安全和稳定性问题,通常可直接升级。灵活的版本策略让用户可在“长期稳定”与“追求新特性”之间选择最合适的版本。

图 . MariaDB 的生命周期。

2. 笔者使用的容器服务·

2.1 Mattermost·

Mattermost 是一个开源的、可自托管的、类似 Slack 的团队沟通、协作软件。Mattermost 是笔者来到英国之后首先接触到的团队协作软件,它并不是一个社交软件,因为它的账户属于邀请制。根据官方描述,在容器化部署之前,首先要拉取容器化部署相关的仓库,并进行简单修改。

1
git clone https://github.com/mattermost/docker

该仓库提供了一个 env.example 文件,用户可以根据这个文件定制自己需要的运行时参数:

1
2
3
4
5
6
7
DOMAIN=app-mattermost.littlenewton.cn
HTTP_PORT=80
HTTPS_PORT=443
MATTERMOST_IMAGE_TAG=10.11.5
MATTERMOST_IMAGE=mattermost-enterprise-edition
POSTGRES_IMAGE_TAG=13-alpine
...

2.1.1 现状分析·

此前笔者一直私有化部署并使用 Mattermost,最近偶尔看到 PostgreSQL 13 还有两三天就彻底 EOL,于是赶忙升级数据库。为了更好地面向未来,笔者决定直接从 pg13 直接升级到最新的 pg18.

方法论

理论上数据库不应该容器化,因为数据库严重依赖后端存储,而且 K8S 的弹性伸缩对数据库也不起作用。过于简洁的容器环境对调试、升级、备份数据库反而是个麻烦事。不过事已至此,笔者只能硬着头皮上。在开始之前简单介绍一下数据库容器化的弊端:

  • PostgreSQL 的文件权限比较严格,在容器内更改文件权限可能受限;
  • 数据库升级、备份和故障排查更复杂;
  • 若未映射持久卷,数据会随容器销毁而丢失。
  • PostgreSQL 镜像内部一般只有一个版本,如果要进行升级,则需要安装多个版本的 PostgreSQL. (虽然复杂了一些,但是在某些情况下这个弊端反而带来了一些优势,比如安装其他版本的 PostgreSQL 时无需添加 PostgreSQL 上游仓库信息。)

下面简单介绍如何升级与更新。

pg13 的数据目录在 /var/lib/postgresql/data, 然而 pg18 要求使用 version-specific 目录 (/var/lib/postgresql/<major>/main),如 /var/lib/postgresql/18/main. 该目录由环境变量 PGDATA=/var/lib/postgresql/18/main 唯一确定。由此我们也也发现了容器化的问题:如果要在同一个容器里进行 pg_upgrade, 则

  • pg13 需要挂载 /var/lib/postgresql/data
  • pg18 要创建 /var/lib/postgresql/18

两者的挂载路径产生交叠,容易引发非确定性的问题。为了避免更进一步的麻烦,笔者选择使用 pg_dumppg_restore.

2.1.2 准备环境·

首先,原本的容器基于 postgres:13-alpine, 这个精简版系统安装软件包非常麻烦,为此我们先换为 postgres:13-trixie,然后仅启动 compose 中的 PostgreSQL 数据库。进入数据库之后首先将备份文件写入 /tmp 目录。一般情况下容器的 /tmp 目录和宿主机 /tmp 是一致的(-v 导入持久卷)。执行下列命令导出 Mattermost 数据库为文件。

1
pg_dump mattermost -U mmuser --no-privileges > /tmp/postgres13-update-backup.sql

随后,我们即可修改 Mattermost 的 compose 文件:

  • postgres:13-alpine 标签改为 postgres:18-trixie.
  • 另外,pg18 的挂载方式有了显著变化:应该将本地的 postgresql 目录挂载到容器内的 /var/lib/postgresql.

随后仅启动 Mattermost compose 的 PostgreSQL 服务:

1
2
3
4
5
6
7
8
9
10
docker compose up -d postgres

# 删除旧的数据库 - 可选
docker exec -it mattermost_db dropdb mattermost -U mmuser

# 创建空白数据库并指定超级用户
docker exec -it mattermost_db createdb mattermost -U mmuser

# 导入备份数据
docker exec -i mattermost_db psql -U mmuser -d mattermost < /tmp/postgres13-update-backup.sql

至此,数据库升级完毕!随后更改 Mattermost 版本标签,然后直接启动新版 Mattermost 即可!

温馨提示

在这个过程中,很有可能需要时可查看文件权限属性,因此直接使用 ls 命令不合适,因为 ls 会做 UID 到用户名的映射。在 TrueNAS 等系统上,999 被分配给了 netdata 用户,这显然是很不常规的。使用 --numeric-uid-gid 可以精准查看具体的 UID/GID.

1
ls -l --numeric-uid-gid

2.2 PhotoPrism·

PhotoPrism, 成功升级 Mariadb 11.8 长期支持版,可以维持两三年时间。升级成功!

MariaDB 的升级貌似要更容易一些。

2.3 NextCloud·

NextCloud, 目前使用 MariaDB 10.6,长期支持版,但是好景不长,根据 https://endoflife.date/mariadb 说明,还有八个月的支持周期。因此在官方推出新版 docker compose 之前,保持现状是最稳妥的。

2.4 Harbor·

Harbor 使用了自己提供的特殊版本 PostgreSQL, 目前最新版为 pg15, 属于一个不新也不旧的版本。Harbor 的升级也能自动化处理,只要提供新版的 Docker Compose 脚本,Harbor 可以自动完成数据库升级。当然,Harbor 最麻烦的并不是数据库本身,而是它需要多级操作才能创建服务:

  1. 下载模板
  2. 编辑制导 yaml 文件
  3. 根据 yaml 文件生成 compose 文件
  4. 启动服务

由于 Harbor 依赖模板文件里的几个配置文件,因此需要在存储路径上做很多手动操作,比如拷贝配置文件到指定路径,已经修改文件权限为 0755 等。相比之下,数据库操作可谓是非常简单,直接交由 Harbor 自己处理即可。

2.5 Immich·

目前依赖 pg14, 同时依赖许多插件,因此官方提供了一个特殊版本的 pg14. 然而,Immich 服务在笔者的系统上已经无法使用,从外部图像库导入图片基本上全部失败,估计是软件有问题。Immich 升级还要留待明日。

总结·

此番数据库升级,让笔者重新审视了路径映射已经 ZFS 数据集命名规则以及 recordsize 设置的问题。总体来看收益还是很明显的。