如何对 SQLite 数据库进行增量备份?

10

我希望能够高频备份数据库,但全备份的成本过高。似乎SQLite没有直接进行增量备份的API,但我发现有数据更改通知回调函数可能会有所帮助。 https://www.sqlite.org/c3ref/update_hook.html 回调函数的参数是操作类型、数据库名称、表名和行ID。然而,我不知道是否可以通过这些信息生成备份信息(例如SQL语句),而无需了解表的详细信息。也就是说,是否存在一种通用方法,适用于具有不同结构的表,以生成此操作的备份? 我知道有一些例外情况,回调函数不会被调用,但如果我定期进行完全备份,那么这是可以接受的。


SQLite有一个备份API:http://sqlite.org/backup.html - Colonel Thirty Two
@ColonelThirtyTwo 这可以进行完整备份。 - CL.
是的。而且频繁进行完整备份成本太高了。 - iuradz
可能是如何创建MySQL数据库的增量备份的重复问题。 - Paul Sweatte
可能是如何为SQLite进行增量备份?的重复问题。 - Bernardo Ramos
可能是有没有一种方法可以使用增量更新SQLITE数据库?的重复问题。 - Paul Sweatte
2个回答

10
我一直在思考这个想法,虽然它没有得到SQLite的正式认可,但从理论上来说,它听起来非常合理。
SQLite备份API基本上允许您获得实时数据库文件的工作快照。同样,VACCUM INTO允许您更新现有的备份数据库文件。https://www.sqlite.org/lang_vacuum.html#vacuuminto 这只是一个好的备份,我们需要增量备份(有点像git)
假设我们想每小时备份一次数据库,它是一个1GB的数据库,写入相对较少,每天存储24GB似乎有些过度kill。
我们可以利用SQLite文件格式,它实质上是一个固定的100字节头+(page_size * num_pages)。 SQLite总是在页面边界周围写入。 page_size和num_pages存储在100字节头中。请参见存储规范https://www.sqlite.org/fileformat.html

那么我们可以创建一个引用文件,它只是一个哈希列表文件。假设我们使用 sha256(这是新版 git 使用的),因此它将是一个文件(例如 backups/2020-02-22-19-12-00.txt)。

sha256(header)
sha256(page1)
sha256(page2)
sha256(page3)

我们将相应的页面存储为单个文件,就像Git在对象目录中一样。例如,objects/ab/cdef12343..。前两个字母用作目录名称,以便我们不会在一个目录中有太多文件。或者,您可以将页面文件上传到任何云存储提供商(例如GCS、S3、Azure Blobs、DO spaces)。这可以提供多地区备份。由于我们不存储页面的重复副本,所以所有备份的总文件大小与database_size*num_backups相比非常小。您甚至可以使用哈希文件来同步/恢复SQLite文件。这就是Dropbox/rsync同步文件的方式。哈希文件告诉我们哪些页面发生了变化,我们只下载已更改的对象并更新文件中的这些范围。

这种方法看起来很有趣。有人认为在数据库使用时还有办法使它工作吗?因为使用上述解决方案,如果在我们对页面进行哈希操作时页面发生更改,则无法正常工作。 - Davi Doro
@user3893988 - 您的答案已在实际实现中进行了翻译:https://github.com/nokibsarkar/sqlite3-incremental-backup - undefined

2
我基于@user3893988的方法创建了一个nodejs包sqlite3-incremental-backup。您可以按以下方式将其用作模块:
import {backup, restore} from './sqlite3-incremental-backup';

const srcFile = 'source.db';
const targetFile = 'target.db';
const snapshotName = 'snapshot1.txt'; //Can be any arbitrary name. MUST BE UNIQUE FOR EACH SNAPSHOT OTHERWISE THE PREVIOUS WILL BE LOST
backup(srcFile, snapshotName); // For Backup
restore(snapshotName, targetFile); // For Restoration

Python模块即将推出。


你是否知道,仅仅复制一个正在使用(生产中)的SQLite数据库文件可能会导致数据库文件损坏,如此处所述:https://sqlite.org/forum/forumpost/58a4ae848fad4a3b - Toxiro
这也是为什么 VACUUM INTO备份 API 被实现的原因之一。仅仅对 SQLite 数据库进行简单的文件复制是不可信的。 - Toxiro
1
重新思考一下,你可能可以将两种解决方案结合起来,首先在本地进行完整备份,然后与上次备份进行差异比较,只将差异存储在远程。这可以使用差异工具甚至在子页面级别上完成。对于庞大的SQLite数据库,您可以使用备份API在多个步骤中备份数据库,同时让用户在其中访问数据库。 - Toxiro
但是你可以使用 https://litestream.io 并拥有一个实时副本,随时可以用来进行恢复。 - Toxiro

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接