Django - 有一个体面大小的级联删除,现在我该如何解决这个问题?

Django - There was a decent size cascade delete, now how do I fix this?

在我的 Django Web 应用程序中,我让用户删除了一些看起来可以安全删除的记录。事实证明,它们通过 ForeignKeyField 字段连接到相当大的其他非常重要的记录集。我现在知道我可以管理如何处理删除,但这个知识是在 这个事件发生之后。

我的主要问题是:有没有一种简单的方法可以解决这个问题,还是只需从备份中逐条小心地恢复每条记录?

更多详情

每天晚上,我都会使用 mysqldump 备份 MySQL 数据库。所以我在这件事发生的前一天备份了所有数据。问题是这些备份文件将在 完整 中恢复数据库。鉴于我们在一周左右的时间里没有注意到这个问题,我认为恢复整个数据库不是一种选择,因为它会覆盖从删除发生之日到现在的其他合法更改。

认为我唯一的选择是手动,一条一条,从MySQL转储文件中挑选出记录,然后手动INSERT它们回到 MySQL 数据库。这感觉像是个坏主意,因为它很容易出现人为错误——我自己打字。

这是唯一的方法还是有更好的方法?!

我将采用的方法是将备份还原到不同的 数据库。 (我们配置每晚 mysqldump 作业的方式,为每个数据库单独转储,转储文件中的 SQL 不包含对数据库名称的任何引用,因此我们很容易创建新数据库,例如

CREATE DATABASE restore_YYYYMMDD_dbname ;

然后 运行 将 mysqldump 压缩到新的 "restore" 数据库中:

gunzip -c dbname.backup_YYYMMDD.sql.gz | \
  mysql u root -pSECRET -c --database restore_YYYYMMDD_dbname 

显然,我们需要足够的磁盘 space,无论它启动多长时间,它都会启动。

然后我可以写SQL来发现删除的行。由于我们在几乎每个 table 中都有一个唯一的 id 列作为主键,我们只需使用反连接来查找恢复的 table 中没有对应行的行在当前数据库中 table

例如:

SELECT r.*
  FROM restore_YYYYMMDD_dbname.mytable r
  LEFT
  JOIN dbname.mytable t
    ON t.id = r.id
 WHERE t.id IS NULL

我们可能不想恢复这些行中的每一行,我们可以调整查询以向 WHERE 子句添加一些额外的谓词以将其还原为我们真正想要的行。然后我们可以将该查询用作 INSERT ... SELECT

的来源
INSERT INTO dbname.mytable 
SELECT r.*
  FROM ...

我们必须按正确的顺序执行每个 table,这样我们就不会违反外键约束。 (我们可以使用 SET FOREIGN_KEY_CHECKS=0,如果我们确定我们知道我们在做什么;但以正确的顺序执行操作更安全。

查找 "changed" 行比删除行稍微复杂一些,但我们可以做同样的事情,也可以编写查询来做到这一点。


我们设置 mysqldump 进程的方式非常简单。这仍然是一个手动过程,但我们让 SQL 为我们完成了很多繁琐的工作。

如果您还没有测试过将数据库从 mysqldump 恢复到其他数据库,您可能想先在不同的环境中进行测试,以免无意中弄乱当前数据库。