ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

Databasus 内部存储引擎选型解析:为何选择内置 PostgreSQL 而非 SQLite(ADR-0002 深度解读)

Databasus 内部存储引擎选型解析:为何选择内置 PostgreSQL 而非 SQLite(ADR-0002 深度解读) 数据库灾备【免费下载链接】databasusPostgreSQL backup tool with Point-In-Time-Recovery and restore verification项目地址https://gitcode.com/gh_mirrors/po/databasus点击查看免费下载Databasus 是一个自托管、开源的 PostgreSQL 备份工具同时支持 MySQL、MariaDB、MongoDB它的核心能力是定时备份、Point-in-Time Recovery 与恢复验证。与所有有状态服务一样Databasus 自身也需要一个内部数据存储来保存调度计划、备份元数据和用户账户。本文基于仓库中的架构决策记录 ADR-0002: Built-in PostgreSQL instead of SQLite完整还原这一决策的来龙去脉并结合当前仓库的 docker-compose.yml、Dockerfile、docker/start.sh、后端配置源码 与 数据库迁移脚本深入讲解元数据库的落地形态、连接配置方式、安全加固与运维要点。读完后你将理解 Databasus 为什么选 PostgreSQL 而不是 SQLite、它如今是如何把 PostgreSQL「塞进」一个容器的、如何切换到外部托管 PostgreSQL以及如何备份这个元数据库本身。决策背景备份工具自己也要一个「数据库」Databasus 需要持久化的内部状态至少包括三类数据调度计划schedules每个数据库实例的备份节奏、时区、cron 表达式备份元数据backup metadata每次备份的大小、耗时、状态、失败原因、存储位置、保留策略用户账户users邮箱、密码哈希、角色权限以及后续演进的 Two-Factor 信息、工作空间与审计日志。这些数据构成了产品自身的运行骨架因此「用什么存储、怎么随产品一起交付」就成了一个必须提前拍板的架构问题这正是 ADR-0002 要回答的问题。注意这个内部存储与「被备份的数据库」是两回事被备份的是用户自己的 PostgreSQL / MySQL / MariaDB / MongoDB 实例而这里讨论的是 Databasus 应用进程自身持久化状态所用的存储引擎。决策内容PostgreSQL 作为内部数据存储ADR-0002 的结论非常直接我们使用 PostgreSQL 作为内部数据存储并在docker-compose.yml中把它作为 Databasus 应用旁边的第二个服务一起交付。这一决策与配套的 ADR-0001: Ship as a Docker image 是一对ADR-0001 决定了部署单元是 Docker 镜像ADR-0002 则在「Docker 化交付」的前提下决定了内部状态落在 PostgreSQL 里。ADR-0001 中同样提到了「一个 Go 后端Gin GORM与嵌入式调度器 React 前端静态资源 PostgreSQL 工具」的组合而 ADR-0002 进一步明确了这些工具所依赖的元数据库引擎。从当前仓库的 docker-compose.yml 可以看到这一决策在开发环境的直接落地# Backend dev services # For development only, because this DB exposes its port to the public backend-db: image: postgres:17 ports: - ${BACKEND_DB_PORT:-5437}:5432 environment: - POSTGRES_DB${DEV_DB_NAME} - POSTGRES_USER${DEV_DB_USERNAME} - POSTGRES_PASSWORD${DEV_DB_PASSWORD} volumes: - /var/lib/postgresql/data container_name: backend-db shm_size: 10gb # Dedicated test DB so make test doesnt pollute the dev DB backend-db-test: image: postgres:17 ports: - ${BACKEND_DB_TEST_PORT:-5438}:5432 environment: - POSTGRES_DB${DEV_DB_NAME} - POSTGRES_USER${DEV_DB_USERNAME} - POSTGRES_PASSWORD${DEV_DB_PASSWORD} volumes: - /var/lib/postgresql/data container_name: backend-db-test shm_size: 10gb可以看到开发与测试各有一个独立的 PostgreSQL 17 容器backend-db供本地开发backend-db-test供测试套件使用避免make test污染开发库端口分别默认为 5437 和 5438数据库、用户名、密码全部通过环境变量注入。仓库根目录的 .env.example 给出了这些变量的开发默认值# dev DB (used by docker-compose.yml and backend) DEV_DB_NAMEdatabasus DEV_DB_USERNAMEpostgres DEV_DB_PASSWORDQ1234567 DATABASE_DSNhostlocalhost userpostgres passwordQ1234567 dbnamedatabasus port5437 sslmodedisable DATABASE_URLpostgres://postgres:Q1234567localhost:5437/databasus?sslmodedisable # test DB (used by backend when IsTestingtrue so tests dont pollute the dev DB) TEST_DATABASE_DSNhostlocalhost userpostgres passwordQ1234567 dbnamedatabasus port5438 sslmodedisableDATABASE_DSN是后端连接元数据库的唯一入口这一点在 backend/internal/config/config.go 中被声明为必填项env:DATABASE_DSN required:true并注册了github.com/jackc/pgx/v5/stdlib作为数据库驱动见 config.go。被否决的备选方案ADR-0002 明确列出了三个候选并给出取舍理由方案结论理由SQLite 嵌入式存储否决无需独立服务、磁盘上只是一个文件但存在单写入者限制与增长期问题PostgreSQL 作为兄弟 compose 服务采纳与本次 ADR 的决策一致让用户自行选择 SQLite 或 PostgreSQL否决单一受支持的存储引擎可以让测试矩阵与支持负担保持最小第三个备选方案的否决值得特别留意它体现了「单一存储引擎」的工程哲学——如果同时支持两种引擎CI 测试矩阵、迁移脚本、文档与排障路径都要翻倍这对一个自托管备份工具来说是不划算的复杂度。当前仓库的开发 compose 中依然只有 PostgreSQL 一种元数据库形态与这一取舍完全吻合。从 ADR 到现在的实现演进兄弟服务变为「容器内嵌」值得指出的是ADR 记录于 ~2024-06-01而当前仓库的实现已经在此基础上演进了一步开发环境维持 ADR 原始形态docker-compose.yml 中backend-db/backend-db-test仍是与应用并列的兄弟 PostgreSQL 服务运行时镜像不再依赖外部 compose 服务而是把 PostgreSQL 17直接安装进应用镜像由容器启动脚本负责引导集群。这一演进的证据在 Dockerfile 中运行时阶段基于debian:bookworm-slim通过 apt 仓库安装postgresql-17包并删除包默认创建的17/main集群因为应用会从/databasus-data/pgdata运行自己的集群默认集群永远不会被读取RUN set -eux; \ apt-get update; \ apt-get install -y --no-install-recommends \ ca-certificates gosu \ libncurses5 libncurses6 libmariadb3 libgnutls30 \ wget; \ wget -qO /usr/share/keyrings/pgdg.asc https://www.postgresql.org/media/keys/ACCC4CF8.asc; \ echo deb [signed-by/usr/share/keyrings/pgdg.asc] http://apt.postgresql.org/pub/repos/apt bookworm-pgdg main \ /etc/apt/sources.list.d/pgdg.list; \ apt-get update; \ apt-get install -y --no-install-recommends postgresql-17; \ pg_dropcluster --stop 17 main; \ ...容器入口是 docker/start.sh其启动流程完整呈现了「内嵌 PostgreSQL」的生命周期reject_legacy_postgresus_volume configure_runtime_identity generate_frontend_runtime_configuration prepare_and_verify_storage bootstrap_postgresql reject_legacy_wal_configuration exec gosu databasus ./main其中bootstrap_postgresql依次完成初始化集群start.sh若/databasus-data/pgdata/PG_VERSION不存在则用initdb创建数据目录--encodingUTF8 --localeC.UTF-8 --usernamepostgres并追加port 5437、listen_addresses localhost、shared_buffers 256MB、max_connections 100等参数——注意监听地址被限定为localhost端口与开发 compose 保持一致5437写入引导期认证配置start.sh引导阶段临时允许databasus系统账户通过 Unix socket 以peer方式登录postgres角色同时用scram-sha-256约束环回 TCP 连接并reject 一切复制连接启动并等待就绪start.sh以后台进程拉起postgres -D /databasus-data/pgdata -p 5437 -k /databasus-data/pgsocket通过pg_isready轮询就绪若启动失败还会走pg_resetwal的 WAL 重置恢复路径start.sh设置密码并建库start.sh用/dev/urandom生成 32 字节随机密码ALTER USER postgres WITH PASSWORD ...并幂等创建databasus数据库切换为运行时认证并自检start.sh移除引导期 peer 映射仅保留「环回 TCP SCRAM」认证并验证「无密码 socket 登录必须被拒绝、携带生成密码的环回登录必须成功」导出应用 DSNstart.sh把hostlocalhost userpostgres password随机密码 dbnamedatabasus port5437 sslmodedisable发布到/dev/shm/databasus-database-dsn内存文件系统容器停止即消亡。这套「单容器内嵌 PostgreSQL」的实现本质上正是 ADR-0002 所追求的「PostgreSQL 引擎 无单写限制 可远端化」在当前交付形态下的极致化用户通过 README 中的一条docker run就能获得完整产品元数据库随容器一起启动无需额外编排。元数据库结构goose 迁移驱动的一整套业务表Databasus 的元数据库 schema 完全由 backend/migrations 目录下的 goose 迁移脚本管理。首份迁移 20250605090323_init.sql 定义了核心实体表从源码结构可以清晰看出业务域表用途关键字段节选users用户账户email唯一索引、hashed_password、rolenotifiers/telegram_notifiers/email_notifiers通知渠道notifier_type、bot_token、target_chat_id、smtp_*storages/local_storages/s3_storages备份存储目标type、s3_bucket、s3_region、s3_access_keyintervals调度节奏interval、time_of_day、weekday、day_of_monthdatabases/postgresql_databases被备份的数据库实例type、host、port、username、cpu_countbackups备份记录status、fail_message、backup_size_mb、backup_duration_msrestores恢复记录backup_id、restore_duration_ms迁移脚本大量使用 UUID 主键、外键约束与联合索引例如backups表上(database_id, created_at DESC)与(status, created_at DESC)两个复合索引直接服务于「最近备份列表」与「失败状态查询」这两类高频读取。之后 60 多份迁移持续演进这个 schema覆盖 webhook、Slack/Discord/Teams 通知、Google Drive/NAS/Azure/FTP/rclone/SFTP 存储、工作空间、双因素认证、物理备份WAL 属性、时间线历史、恢复验证等能力足见元数据库是整个产品功能面的物理载体。值得一提的还有测试侧的工程细节为了不让并行测试互相污染元数据库backend/internal/config/config.go 实现了「按测试 worker 划分 slot 数据库」的机制——每个go test并行 worker 通过在postgres系统库上抢占pg_try_advisory_lock锁基址945_000_000见 config.go领取一个空闲 slot再切换到databasus_wN专用库执行测试。这与 ADR-0013: Reuse shared in-memory test databases 描述的共享测试数据库策略一脉相承也印证了「单一存储引擎让测试矩阵保持可控」这一 ADR 决策红利。连接配置DATABASE_DSN 与外部 PostgreSQL 的无缝切换ADR-0002 的正向后果中特别提到「我们可以把系统连接到外部 PostgreSQL相同引擎意味着把内置实例换成托管实例只是一个配置变更而不是一次重写」。当前代码将这一点实现得相当彻底。在 backend/internal/config/config.go 中DSN 的解析优先级是操作者显式提供的DATABASE_DSN环境变量最高优先级见 config.go容器启动时发布到/dev/shm/databasus-database-dsn的生成 DSNconfig.go镜像内烘焙的/.env默认值最低优先级且其中的开发用DATABASE_DSN会被 Dockerfile 显式删除避免默认凭证参与认证。也就是说只要操作者在容器环境里设置一个指向外部 PostgreSQL 的DATABASE_DSN启动脚本的configure_application_database_dsnstart.sh就会直接跳过生成与发布逻辑应用整个生命周期都使用操作者提供的连接串——「换掉存储引擎」在这里真的只是一个环境变量。反过来如果不提供任何 DSN则所有进程统一使用启动时生成的随机密码 DSN镜像中不存在任何可用的默认凭证这也在 openspec/specs/internal-postgresql-protection/spec.md 的「Internal PostgreSQL credentials are generated at startup」需求中固化为验收标准。对于使用内置元数据库的场景这一机制还保证了docker exec进入正在运行的容器执行控制台命令如 README 中描述的重置密码命令./main --new-password...时能拿到与主应用一致的连接串因为/dev/shm/databasus-database-dsn会随每次容器启动重新生成且容器重启会轮换内部密码旧密码永远无法复用。安全加固内部 PostgreSQL 的边界防护「元数据库与被备份数据库共用同一套连接基础设施」带来了一个必须解决的攻击面用户能否把 Databasus 的备份/连接测试目标指向它自己的元数据库从而读取其他工作空间的凭证仓库通过 openspec/specs/internal-postgresql-protection/spec.md 这一开放规范明确定义了防护要求要点包括逻辑备份不得指向内置元数据库当逻辑备份源的数据库名为databasus且有效 host 指向容器自身文件系统/抽象 Unix socket、环回地址、容器别名、空 host 项、多 host 列表中的任一本地项时系统必须在打开连接前拒绝该配置物理备份不得指向内置集群物理备份按「本地地址 内置端口5437」组合判定拒绝不依赖数据库名因为物理备份面向整个集群连接值保持字面语义用户可控的 host、用户名、密码、数据库名等字符串必须作为单个值传给 PostgreSQL 连接解析器不得因包含 conninfo 元字符空白、引号、dbname等而伪造出新参数防止把连接重定向到元数据库连接时强制校验即使配置是策略生效前保存的旧行也必须在真正打开连接的那一刻重新执行上述规则模型校验不是唯一的执行点SSH 隧道场景经远端 bastion 隧道访问时环回地址按远端语义解释本地 bastion 指向容器自身时仍然拒绝。与这份规范对应的落地代码在 docker/start.sh 的运行时pg_hba.conf中同样清晰可见local all all rejectUnix socket 登录一律拒绝、host all all 127.0.0.1/32 scram-sha-256环回 TCP 仅接受 SCRAM 认证、host replication all ... reject复制连接彻底禁止。镜像也不会把 5437 端口暴露到宿主机Dockerfile 仅EXPOSE 4005因此元数据库只能通过环回或受控的容器内部路径访问。正向后果与代价ADR 给出的权衡清单ADR-0002 把选型的影响摆得很坦诚结合当前实现可以逐条验证正向后果未来需要时支持远程访问PostgreSQL 天然支持网络暴露无需更换存储引擎。当前实现为安全已将监听限制在localhoststart.sh但引擎能力本身不受限未来需要时支持高并发写入PostgreSQL 处理并发写者没有 SQLite 的单写者限制。Databasus 的调度器、后台任务、多用户操作共享同一个元数据库并发路径天然存在未来需要时可使用扩展、复制与更丰富的查询能力数据库引擎是 PostgreSQL从 schema 迁移goose到复制拓扑都有现成工具链可连接外部 PostgreSQL如上一节所述DATABASE_DSN一个变量即可完成切换主观经验ADR 作者Rostislav记录了 SQLite 在项目增长期的远程访问、备份与多写者问题认为「一开始就选更重的选项可以避免将来被迫迁移」。负面代价需要运维一个独立服务相比 SQLite 的单文件PostgreSQL 有独立镜像、数据卷与生命周期。当前实现通过「内嵌进应用容器」显著摊薄了这一成本用户感知上仍然是一条docker run备份元数据库更复杂活着的 PostgreSQL 实例的备份比复制单个文件更讲究——这直接呼应了 README 中「建议备份 Databasus 自身」的运维指引见 README.md。操作者需要理解数据目录/databasus-data/pgdata与 secret key/databasus-data/secret.key的关系并按 PostgreSQL 的规范方式如pg_dump或文件级快照处理一致性额外的 CPU 与内存开销ADR 明确评估「开销小到在目标主机上可承受」当前实现给出的参考参数是shared_buffers 256MB、max_connections 100start.sh这是一个对单机自托管场景相当克制的配置。运维视角如何查看、备份与切换元数据库基于以上实现操作者可以按如下方式与元数据库打交道开发环境连接docker compose up -d backend-db后用.env中的凭据连接localhost:5437数据库名databasus生产容器内连接进入运行中的容器执行psql二进制位于/usr/lib/postgresql/17/bin通过/dev/shm/databasus-database-dsn读取当前密码后走环回 TCP 连接-h localhost -p 5437 -U postgres -d databasus备份元数据库遵循 README 的建议备份databasus-data卷至少包含secret.key与pgdata或对运行中的内置实例执行pg_dump注意内置实例拒绝复制连接因此不应尝试流复制方案切换到外部 PostgreSQL设置DATABASE_DSN指向托管实例例如 RDS、自建 PG 或云数据库并确保先通过 goose 迁移把 schema 建好即可让 Databasus 的全部状态落到外部引擎上。结论ADR-0002 是一个「为未来买保险」的架构决策Databasus 放弃 SQLite 的单文件简洁选择 PostgreSQL 作为内部数据存储换取远程访问、并发写入、扩展生态与「外部托管引擎只需改配置」的灵活性。从当前仓库看这一决策不仅被完整执行还演化出了更优的交付形态——开发环境维持 compose 兄弟服务运行时镜像则把 PostgreSQL 17 内嵌进应用容器并通过内存文件发布轮换密码配合 内部 PostgreSQL 保护规范 与严格的pg_hba.conf在「自托管易用性」与「元数据安全」之间取得了平衡。如果你正在自托管 Databasus理解这条 ADR 就能明白为什么备份工具自己也需要备份为什么DATABASE_DSN一个变量就能决定引擎去向以及为什么内置实例的 5437 端口永远不应该暴露到公网。赞分享数据库灾备【免费下载链接】databasusPostgreSQL backup tool with Point-In-Time-Recovery and restore verification项目地址https://gitcode.com/gh_mirrors/po/databasus点击查看免费下载相关推荐ADR 001: 选择SQLite而非PostgreSQL的理由ADR 001: 选择SQLite而非PostgreSQL的理由 决策日期2025 01 15 背景项目初期用户量预估100需降低部署复杂度 后果优势知识库文档嵌入式调试从入门到量产我如何用pyOCD打通Arm Cortex-M开发全流程嵌入式调试从入门到量产我如何用pyOCD打通Arm Cortex M开发全流程 如果你刚接手一个 Arm Cortex M 项目第一次接触嵌入式调试多半会嵌入式开发工具调试器react-starter-kit 的状态管理决策为什么选择 HCP Terraform 而非对象存储ADR-003 深度解读react starter kit 的状态管理决策为什么选择 HCP Terraform 而非对象存储ADR 003 深度解读 导读 本指南围绕 doc后端前端上一篇RePKG工具深度解析解锁Wallpaper Engine隐藏资源的神器下一篇快速掌握Blender 3MF插件新手终极指南创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表