January 30, 2023
By: Kevin'

SQLite 数据库备份全攻略

  1. 直接复制文件 (CP 命令)
  2. .clone 命令
  3. .dump 命令
  4. VACUUM INTO 命令
  5. .backup 命令
  6. litestream (第三方工具)
  7. rsync 命令
  8. robocopy 命令
  9. 总结

数据备份的重要性不言而喻. 对于轻量级数据库 SQLite 来说, 备份操作更是至关重要. 本文将深入探讨 SQLite 数据库的多种备份方法, 并分析它们的优缺点.

SQLite 的魅力之一在于其数据存储在一个单一文件中. 在理想情况下, 可以直接复制该文件进行备份. 然而, 现实情况往往更加复杂. 本文逐一了解各种备份方法, 并提供清晰的代码示例.

在开始之前, 先使用 time 命令来衡量各种备份操作的耗时, 以便更直观地比较它们的效率. (注: 以下所有命令示例均以 time 命令开头, 用于记录执行时间.)

直接复制文件 (CP 命令)

方法描述:

最简单粗暴的方法, 就像复制普通文件一样, 直接使用 cp 命令复制 SQLite 数据库文件.

示例代码:

time cp imdb.db imdb_backup.db

优点:

  • 简单快捷, 操作门槛低.

缺点:

  • 数据一致性风险: 如果数据库在备份时正在进行事务处理, 复制的文件可能包含新旧数据混合, 导致数据损坏.
  • 不适用于在线数据库: 当数据库处于打开状态并有连接时, 直接复制文件极易造成数据损坏.

结论: 仅适用于数据库完全离线且对数据一致性要求不高的极少数场景. 强烈不推荐在生产环境中使用.

.clone 命令

方法描述:

使用 SQLite 命令行工具提供的 .clone 命令, 可以将整个数据库克隆到一个新文件中.

示例代码:

time sqlite3 imdb.db ".clone clone.db"

优点:

  • 操作简单, 使用方便.

缺点:

  • 速度慢: 克隆大型数据库耗时较长.

  • 数据库锁: 克隆操作会锁定数据库, 阻止写入操作. 在克隆期间尝试写入数据库会导致错误, 例如:

    sqlite3 imdb.db "UPDATE titles SET title = 'Updated Title' WHERE title_id = 1;"
    Error: database is locked
    

    读取操作仍然允许, 例如:

    sqlite3 imdb.db "SELECT title FROM titles LIMIT 10;"
    

结论: 虽然比直接复制文件安全一些, 但速度慢且锁定数据库使其在在线备份场景中不可用. 不推荐用于生产环境备份.

.dump 命令

方法描述:

.dump 命令会将整个数据库(包括 schema 和数据)导出为 SQL 文本文件.

示例代码:

time sqlite3 imdb.db ".dump" > imdb.sql

优点:

  • 导出的 SQL 脚本具有良好的可读性, 方便查看和编辑.
  • 可以用于跨平台和 SQLite 版本的数据迁移.

缺点:

  • 速度慢: 导出大型数据库为 SQL 脚本耗时较长.
  • 数据库锁: 导出操作同样会锁定数据库, 阻止写入操作.
  • 恢复速度慢: 恢复数据库需要执行 SQL 脚本, 重建表结构和导入数据, 耗时较长.

结论: .dump 命令更适合用于数据库迁移, schema 变更管理或生成数据库的文本表示, 不推荐作为主要的备份方案.

VACUUM INTO 命令

方法描述:

VACUUM INTO 命令用于创建一个数据库的真空化副本. 真空化操作可以移除数据库中的未使用页面, 并对数据库进行碎片整理, 从而减小数据库文件大小.

示例代码:

time sqlite3 imdb.db "VACUUM INTO vacuum.db"

优点:

  • 备份速度比 .clone.dump 快.
  • 备份文件可能更小, 节省存储空间.

缺点:

  • 数据库锁: 真空化操作仍然会锁定数据库, 阻止写入操作.

结论: VACUUM INTO 比前几种方法稍好, 但锁定数据库的问题依然存在. 在在线备份场景中仍然不理想.

.backup 命令

方法描述:

.backup 命令是 SQLite 官方推荐的备份方法, 它可以将数据库备份到另一个文件.

示例代码:

time sqlite3 imdb.db ".backup backup.db"

优点:

  • 速度快: 备份速度远快于其他 .clone, .dump, VACUUM INTO 命令.
  • 锁机制更友好: .backup 命令在备份过程中会周期性地释放锁, 允许其他连接进行写入操作 (需要设置合适的查询超时时间).
  • API 支持: 大多数 SQLite 库都提供了相应的 API 来执行备份操作, 方便程序集成.

需要注意的 Quark:

如果在备份过程中, 执行备份操作的数据库连接本身进行了写入操作, 则这些写入操作的数据会被包含在备份中, 这符合预期.

然而, 不同的数据库连接执行了写入操作, 则整个数据库备份操作会被重启. 频繁的写入操作可能导致备份操作被延迟甚至无法完成.

最佳实践: 尽可能使用同一个数据库连接进行备份和写入操作. 但这在某些编程语言和库中可能具有挑战性.

结论: .backup 命令是在线备份 SQLite 数据库的首选方法之一. 它在速度, 锁机制和 API 支持方面都表现出色.

litestream (第三方工具)

方法描述:

litestream 是一款开源工具, 专门用于 SQLite 数据库的近实时流式备份. 它可以将 SQLite 数据库的更改流式传输到 S3 兼容的存储服务, 例如 AWS S3, MinIO 等.

安装指南:

请参考 litestream 官网 获取详细的安装说明.

配置和使用:

  1. 创建 S3 存储桶: 在 S3 兼容的服务上创建一个存储桶用于存放备份数据.

  2. 配置凭证: 创建访问 S3 存储桶所需的凭证, 并将其导出为环境变量.

  3. 运行 litestream 复制命令:

    litestream replicate imdb.db s3://your-s3-bucket/imdb_backups
    

    (示例中使用 MinIO 作为 S3 兼容服务, 可以替换为任何 S3 兼容服务. )

恢复数据库:

litestream restore imdb-ls.db s3://your-s3-bucket/imdb_backups

优点:

  • 近实时流式备份: 持续监控数据库变更并实时备份, 实现 RPO (Recovery Point Objective) 最小化.
  • 无锁备份: Litestream 的备份过程不会锁定数据库, 保证数据库的持续可用性.
  • 快速恢复点: 可以快速恢复到故障发生前的任意时间点.

缺点:

  • 恢复速度可能较慢: 恢复数据库需要从 S3 下载备份数据, 恢复时间取决于网络速度和备份数据大小.
  • 依赖第三方工具: 需要安装和配置 Litestream 工具.

结论: Litestream 是 SQLite 在线备份的最佳选择之一, 尤其适用于对数据可靠性和业务连续性要求极高的场景. 它提供的流式备份和无锁特性, 能够最大限度地保障数据安全和数据库可用性.

rsync 命令

可以使用 rsync 命令来备份 SQLite 数据库。rsync 是一个强大的文件同步工具,可以高效地复制文件和目录,同时保留文件的元数据。

只是rsyncscp类似,只不过是增量的,只复制有变化的文件,是比较理想的。

在windows上使用rsync,需要安装cwRsync,具体安装步骤可以参考这篇文章

安装指南:

请参考 rsync 官网 获取详细的安装说明.

配置和使用:

  1. 在要复制文件的两端都安装ssh-key,且配置好免密登录。
  2. 在两台服务器上分别安装sshd服务器(系统联网就可以),并启动sshd服务。启动后能通过scp命令复制文件到另一台机器则成功。
  3. 两端都安装rsync
  4. 在命令行测试rsync的命令如下,如果同步成功,则配置成功
# 把本地的C盘的abc.txt复制到[192.168.0.23]的C盘的other/dir目录下。
rsync /cygdrive/c/some/dir/abc.txt //192.168.0.23/C/other/dir/

clojure上的util实现如下:

(:import
   (com.github.fracpete.rsync4j RSync Ssh)
   (com.github.fracpete.processoutput4j.output CollectingProcessOutput)
   (com.github.fracpete.rsync4j.core Binaries))

(defn rsync-windows
  "使用 rsync 同步文件到 Windows 目标机器.

  参数:
  - src: 源文件或目录路径 (字符串).
  - dest: 目标 Windows 机器上的目录路径 (字符串, Cygwin 格式, 如 host:/cygdrive/c/path/to/dest).

  使用示例:
  (rsync-windows \"/Users/a123/sandbox/rc/learn-clojure/try-rsync/src/try_rsync/core.clj\" \"10.211.55.14:/cygdrive/c/new/abc\")

  注意:
  - 此函数假定已配置好 SSH 密钥认证, 允许无密码登录到目标 Windows 机器.
  - 源路径 (src) 应该使用本地绝对路径.
  - 目标路径 (dest) 必须使用 Cygwin 风格的路径, 例如 /cygdrive/c/Users/user/Desktop/.
  - 如果ssh的key不是默认位置, 需要通过`.rsh`指定, 例如
  `(.rsh (str (Binaries/sshBinary) \" -i \" (Binaries/convertPath \"/Users/a123/sandbox/rc/learn-clojure/try-rsync/id_rsa.pub\")))`
  - rsync同步是一次性的连接。它会在完成文件同步后自动断开连接(由rsync工具自动管理),因此无需手动关闭
    关闭定时任务即可"
  [src dest]
  (let [item-name "Rsync同步输出,"
        rsync (doto (RSync.)
                (.source src)                 ;; 源路径
                (.destination dest))  ;; 目标路径, 必须是Cygwin格式
        output (new CollectingProcessOutput)]   ;; 创建用于收集输出的对象
    (try
      (.monitor output (.builder rsync))      ;; 执行 rsync 并监控输出
      (debug item-name "Stdout:\n" (.getStdOut output))   ;; 打印标准输出
      (debug item-name "Exit code: " (.getExitCode output)) ;; 打印退出码
      (when (> (.getExitCode output) 0)         ;; 如果退出码大于0, 表示有错误
        (error item-name "Stderr:\n" (.getStdErr output)))   ;; 打印标准错误输出
      (catch Exception e
        (error item-name "执行 Windows 示例时发生错误: " (.getMessage e))))))

(comment
  (def src "/Users/mahaiqiang/git/redcreation/chutian/src/clj-backend/project.clj")
  (def dest "mahaiqiang@192.168.0.140:/cygdrive/c/Users/abc.project")

  (rsync-windows src dest)
  )

robocopy 命令

robocopy是Windows系统自带的文件复制工具,可以用于同步文件和目录。它支持增量复制、断点续传等功能,可以用于实现文件同步。

配置和使用: 在A机器上设置共享文件夹。在B机器上使用robocopy命令进行同步。官方说明

robocopy如果要在程序里执行,也是需要配置账号密码的,出了net use命令,生产上使用的方式应该是用windows的凭据管理器,或者用secrets库来存储密码。

在 Windows 凭据管理器中添加凭据:* * 打开“控制面板” -> “用户帐户” -> “凭据管理器”。 * 点击 “Windows 凭据“。> * 点击 “添加 Windows 凭据“。 * 输入目标计算机的名称或 IP 地址、共享文件夹的用户名和密码,然后点击“确定”。

  • 测试使用
    robocopy "<source replicated folder path>" "<destination replicated folder path>" 
    

    robocopy会自动同步文件,并记录同步状态,且可以增加重试等很多参数也是在官方介绍里有。

在clojure上同步sqlite数据库的utils方法如下:

(defn robocopy-sync
  "使用 Robocopy 复制单个文件。
  参数:
    src-file:  源文件路径(字符串)。
    dest-dir: 目标目录路径(字符串)。  注意:必须是目录!
    options:   一个可选的 map,包含 Robocopy 的选项。
               键是选项的名称(关键字),值是选项的值(字符串或 nil 表示无值选项)。
               例如:{:mirror true, :log \"C:\\log.txt\", :mt 16}

  返回值:
    一个 map,包含 :exit(退出代码),:out(标准输出),:err(标准错误)"
  [src-file dest-dir & [options]]
  (let [base-command ["robocopy"
                      ;; 获取源文件所在的目录
                      (-> src-file (java.io.File.) .getParent)
                      ;; 使用目标目录,不需要包含文件名
                      dest-dir
                      ;; 获取文件名
                      (-> src-file (java.io.File.) .getName)
                      " /Z "] ;;断点续传
        option-flags (reduce-kv (fn [acc k v]
                                  (if (true? v)  ; 如果值是 true,则只添加选项的键(例如 /MIR)
                                    (conj acc (str "/" (name k)))
                                    (if (nil? v)
                                      acc  ;如果值为nil 什么都不加
                                      (conj acc (str "/" (name k) ":" v)))))  ; 如果值不是 true,添加键和值
                                []
                                options)
        command (into base-command option-flags)]
    (info "robocopy命令:" command)
    (try
      (let [result (apply sh command)]
        (if (not= 0 (:exit result))
          (error "Robocopy 执行错误:" (:err result)))
        result)
      (catch Exception e
        (error "调用 Robocopy 失败:" (.getMessage e))
        {:exit -1 :out "" :err (.getMessage e)}))))


(comment

  (robocopy-sync "D:\\chutian\\Batch-package\\readme.txt" "\\\\192.168.0.107\\project\\db\\" {:r 3 :w 5})
  )

总结

本文深入分析了 SQLite 数据库的六种备份方法, 并详细对比了它们的优缺点,基本汇总如下:

备份方法适用场景优势劣势
直接复制文件 (cp)数据库离线,备份快速完成简单,速度快可能导致数据损坏,不适用于在线数据库
.clone 命令需要完整克隆数据库,读操作可进行操作简单,支持 SQLite 命令行速度慢,锁定数据库,影响写入操作
.dump 命令数据迁移,文本备份,可读性强便于跨平台迁移,易于编辑速度慢,恢复耗时,锁定数据库
VACUUM INTO 命令需要优化数据库时进行备份速度较快,可优化数据库,减少文件大小仍然锁定数据库,不适用于高可用场景
.backup 命令在线备份,需最小影响数据库操作速度快,允许部分写入,API 兼容性好频繁写入可能导致备份重启
Litestream需要实时增量备份,支持云存储无锁,实时备份,RPO 最小依赖第三方工具,恢复速度受限于网络
rsync 命令远程增量备份,适用于 Linux 服务器仅同步变化部分,高效可能仍然存在一致性问题,不适用于高并发写入
robocopy 命令Windows 端备份,支持断点续传增量同步,断点续传,适用于大文件需要额外配置,适用于 Windows 服务器

对于大多数场景, .backup 命令和 Litestream 是最佳选择:

  • .backup 命令: 适用于对备份速度有较高要求, 且可以接受短暂锁定的场景.
  • Litestream: 适用于对数据可靠性和业务连续性有极高要求, 需要近实时备份和无锁备份的场景.

希望本文能帮助读者更好地理解 SQLite 数据库备份, 并选择最适合的方案.

Tags: sqlite sql