备份还原失败

我遇到了以下问题。我已经运营 Discourse 论坛超过 10 年,但由于一段时间以来无法安装更新,我打算设置一台新服务器:

旧服务器运行版本:3.4.0.beta4-dev

新服务器:最新版本

备份文件(不包括上传内容)的 .gz 文件大小已达 673.2 MB。

不幸的是,恢复操作不断中断。日志文件中包含以下错误:

[2026-06-16 07:54:52] ERROR:  could not create unique index “index_incoming_referers_on_path_and_incoming_domain_id”
[2026-06-16 07:54:52] DETAIL:  Key (path, incoming_domain_id)=(//, 5) is duplicated.
[2026-06-16 07:54:52] EXCEPTION: psql failed: DETAIL:  Key (path, incoming_domain_id)=(//, 5) is duplicated.

[2026-06-16 07:54:52] /var/www/discourse/lib/backup_restore/database_restorer.rb:93:in 'BackupRestore::DatabaseRestorer#restore_dump'
/var/www/discourse/lib/backup_restore/database_restorer.rb:26:in ‘BackupRestore::DatabaseRestorer#restore’
/var/www/discourse/lib/backup_restore/restorer.rb:61:in 'BackupRestore::Restorer#run'
/var/www/discourse/script/spawn_backup_restore.rb:20:in ‘Object#restore’
/var/www/discourse/script/spawn_backup_restore.rb:33:in ‘block in ’
/var/www/discourse/script/spawn_backup_restore.rb:4:in 'Kernel#fork'
/var/www/discourse/script/spawn_backup_restore.rb:4:in ‘’
[2026-06-16 07:54:52] Trying to rollback...

我该如何解决这个问题,同时不丢失过去 10 年的数据?

提前感谢任何帮助!

你好,哇,欢迎回来,时隔九年!

能看到这样一个运行了这么久的论坛,真的很酷。

我认为这和你在这里讨论的索引问题是同一个:Can't restore due to corrupt indexes (with some clues on how to deal with corrupt indexes)

情况大致如下:

incoming_referers 表用于跟踪将访客引导至你论坛的 URL 路径。它有一个唯一索引,这意味着不能有两行具有相同的路径 + 域名组合。

你的数据库中有两行的 path='//'incoming_domain_id=5。当它在恢复期间尝试重建这个唯一索引时,发现了重复项并中止了整个恢复事务。

因此,你需要找到并清理那个重复的 incoming_referers 条目,然后制作一个新的备份以在新服务器上恢复。

我找到了这个包含说明的主题,可能会对你有帮助。

非常感谢您的快速回复,我会去试试的!

(帖子已被作者删除)

不好意思打扰了,但我好像哪里做错了——执行命令“discourse=# select * from incoming_referers where path LIKE ‘%/search/’ ORDER BY incoming_domain_id;”后,出现了一个语法错误。

你输入的是 “discourse=#” 吗?如果是,请去掉这部分重新输入。

然后我收到了错误消息“bash: syntax error near unexpected token `from’”

作为参考,我当前的位置是:root@community-app:/var/www/discourse#

啊,你还没有进入数据库。

先运行 psql -U discourse discourse(我想应该是这样)

然后提示符应该会变成 discourse=#

好的,我现在也安装了“apt install postgresql-client-common”。不过,我仍然收到错误提示:“错误:您必须至少安装一个 postgresql-client- 包。”

好的,这个我不清楚。

也许可以尝试直接通过 Rails 控制台执行:rails c

然后运行

ActiveRecord::Base.connection.execute(<<~SQL)
  SELECT id, path, incoming_domain_id 
  FROM incoming_referers 
  WHERE path = '//' 
  AND incoming_domain_id = 5
SQL

这样或许能在不经过数据库的情况下得到相同的结果。

这样可行吗?

我真的非常抱歉当时表现得那么愚蠢。

我现在已经安装了 Rails,看起来安装成功了。我也用“rails c”运行了它。那部分似乎也没问题,但当我输入命令时,显示如下:

“root@community:/var/discourse# ActiveRecord::Base.connection.execute(<<~SQL)
-bash: syntax error near unexpected token `<<’
root@community:/var/discourse# SELECT id, path, incoming_domain_id
SELECT: command not found
root@community:/var/discourse# FROM incoming_referers
FROM: command not found
root@community:/var/discourse# WHERE path = ‘//’
WHERE: command not found
root@community:/var/discourse# AND incoming_domain_id = 5
AND: command not found
root@community:/var/discourse# SQL”

别担心,也许是我的说明不够清楚 :slight_smile:

你说刚安装了 Rails 却连不上数据库,这让我有点困惑。也许是因为版本较旧,情况有所不同,但说实话,我现在也有点摸不着头脑了。

总之,进入 Rails 后,提示符应该会发生变化。从你的输出来看,你似乎还没有进入 Rails 控制台。

我甚至觉得你还没进入实际的容器。你执行过 ./launcher enter app 吗?

好的,确实如此,问题就出在这里。现在查询成功执行了,我收到了以下响应:

#<PG::Result:0x00007fbde9732ef0 status=PGRES_TUPLES_OK ntuples=1 nfields=3 cmd_tuples=1>

所以,ntuples=1 表示只找到了一行,但根据错误日志来看,应该存在重复项…… :woman_shrugging:

抱歉,我不知道接下来该尝试什么。我建议你在论坛里逛逛,似乎有很多类似的案例(见本主题下方的相关部分),也许你能在那里找到下一个线索。

(或者等一个更懂行的人路过看看 ;))

听起来您的旧服务器并非标准安装。总之,简要说明一下情况:您的旧服务器上的某个索引在多年间损坏了(操作系统升级后可能发生这种情况),导致两条完全相同的行被插入到 incoming_referers 表中。备份会原样复制它们,而新服务器则会拒绝这些行。因此,我们需要先在旧服务器上修复,然后再创建新的备份。

旧服务器上,打开 Rails 控制台:

./launcher enter app
rails c

然后依次粘贴以下代码行:

db = ActiveRecord::Base.connection.current_database
DB.exec("DELETE FROM incoming_referers a USING incoming_referers b WHERE a.id > b.id AND a.path = b.path AND a.incoming_domain_id = b.incoming_domain_id")
DB.exec("REINDEX DATABASE #{ActiveRecord::Base.connection.quote_table_name(db)}")

这将删除重复项并重建所有索引(以防其他表也受到影响)。

如果 REINDEX 执行完毕且未报错,请输入 exit,在旧服务器上创建新的备份,然后恢复该新文件。如果报错提示涉及其他表,请直接将该错误信息粘贴到这里。

非常感谢。不过,我在最后一行收到了一个错误提示:

“PG::InsufficientPrivilege: 错误:必须是数据库 discourse 的所有者”

按理说,这应该已经是一个“标准服务器”了。当初的安装是由“Discourse”团队一次性付费完成的。

啊,对了。我们试试看。

退出 Rails 控制台(输入 exit),然后在容器内(./launcher enter app)运行:

su postgres -c "reindexdb discourse"

当该命令无错误执行完毕后,在旧服务器上做一个全新备份并恢复这个新文件。如果提示某个特定表的错误,请直接把它贴到这里。