SQLite数据库(备份)

2024-09-03 18:36
文章标签 数据库 备份 sqlite

本文主要是介绍SQLite数据库(备份),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

1.备份分类

1.1文件级备份

直接复制数据库文件(例如  .db文件)的副本。SQLite数据库本质上是一个单一的文件,使得其备份起来相对简单。操作相对来说也比较简单,在数据库关闭时,直接复制文件即可(如使用 cp 命令在UNIX系统上,或使用文件管理工具)。在数据库打开时,可以使用SQLite的备份机制或事务日志数据保持一致性。

优点:操作简单,适用于快速备份和文件恢复。
缺点:如果数据库处于正在被使用或者处于活动状态,可能需要确保数据一致性(使用WAL模式或备份机制)

#include <iostream>
#include <fstream>
#include <string>/* 打开源文件和目标文件,将源文件的内容复制到目标文件中ifstream,ofstream用于文件的读写操作,ios::binary(以二进制的方式)
*/
bool copyFile(const std::string& sourcePath, const std::string& destPath) {std::ifstream src(sourcePath, std::ios::binary);if (!src) {std::cerr << "Error opening source file: " << sourcePath << std::endl;return false;}std::ofstream dest(destPath, std::ios::binary);if (!dest) {std::cerr << "Error opening destination file: " << destPath << std::endl;return false;}dest << src.rdbuf(); // Copy contentreturn src && dest;
}int main() {std::string sourceFile = "source.db"; //替换为源文件路径std::string backupFile = "backup.db"; //替换为备份文件路径if (copyFile(sourceFile, backupFile)) {std::cout << "Backup successful: " << backupFile << std::endl;} else {std::cerr << "Backup failed." << std::endl;}return 0;
}

注:一致性:如果数据库在复制时处于活动状态,可能会导致备份文件不一致。考虑使用SQLite的在线备份工具或者在低流量时进行备份。
存储:将备份文件存储在安全的地方,并定期更新。
恢复:恢复时只需要备份文件替换现有数据库即可。

WAL模式(Write-Ahead Logging) 是一种数据库日志记录机制,在执行数据库写操作之前,首先将操作记录到日志中。这确保了数据的一致性和恢复能力,即使系统崩溃,也可以通过日志完成未完成的操作。
备份机制是指用来保护数据库数据的各种方法和策略,包括文件级备份(直接复制文件),(逻辑备份导出数据结构和内容),增量备份(只备份自上次备份以来的更改)等,以便在数据丢失或者损坏时进行修复。

1.2在线备份

从历史上看,SQLite数据库的备份(副本)是使用以下方法创建的:

1.使用SQLite API(即shell工具)在数据库文件上建立共享锁。
2.使用外部工具复制数据库文件(例如unix的 “cp”实用程序或DOS的“Copy”命令)。
3.解除数据库文件上的共享锁

这个过程在很多情况下都是很有效的,而且通常非常快。然而,这种技术有以下缺点:

任何希望在创建备份是写入数据库文件的数据库客户端都必须等待,直到共享锁被释放。
无法用于向内存数据库复制数据或从内存数据库复制数据。
如果在复制数据库文件时发生电源故障或者操作系统故障,则备份数据库可能会在系统回复后损坏。

为此SQLite创建了在线备份API来解决这些问题。在线备份API允许将一个数据库的内容复制到另一个数据库文件中,替换目标数据库的任何原有内容。复制操作可以增量完成,在这种情况下,源数据库在复制期间不需要被锁定,只需要在实际被读取的短暂时间内被锁定吗。这样就允许其他数据库用户在对在线数据库进行备份时继续操作,而不会出现过多的延迟。

完成备份调用序列的效果是使目标数据库成为源数据库的按位相同的副本,就像开始复制时一样。

SQLite提供了以下三个接口完成:将内存中数据库的内容加载并保存到磁盘上的一个文件中。

sqlite3_backup *sqlite3_backup_init(sqlite3 *pDest,         /* 目标数据库连接 */const char *zDestName,  /* 目标数据库的附加名称 */sqlite3 *pSource,       /* 源数据库连接 */const char *zSrcName    /* 源数据库的附加名称 */
);

注:目标数据库连接和源数据库连接必须不同,否则sqlite3_backup_init()将失败并返回错误。

如果目标数据库上已经打开了一个读或者读写事务,那么调用sqlite3_backup_init()将失败,返回NULL。

如果在sqlite3_backup_init()中发生错误,则返回NULL并将错误代码和错误信息存储在目标数据库连接(pDest)中。调用sqlite3_backup_init()失败的错误代码和消息可以使用sqlite3_errcode(),sqlite3_errmsg和sqlite3_errmsg16函数检索。

成功调用sqlite3_backup_init()会返回一个指向sqlite3备份对象的指针。

int sqlite3_backup_step(sqlite3_backup *p, int nPage);
p: sqlite3_backup 结构体的指针,它是通过 sqlite3_backup_init() 创建的备份操作句柄。
nPage: 要复制的页面数。如果这个值为 -1,函数将复制源数据库中的所有剩余页面。

sqlite3_backup_step()成功复制了nPage个页面,并且还有更多的页面需要复制,那么该函数返回SQLITE_OK。如果sqlite3_backup_step()成功完成从源到目标的所有页面复制,那么它返回SQLITE_DONE。如果运行sqlite_backup_step()时发生错误,则返回错误码。除了SQLITE_OK和SQLITE_DONE,调用sqlite3_backup_step()可能会返回SQLITE_READONLY、SQLITE_NOMEM、SQLITE_BUSY、SQLITE_LOCKED或SQLITE_IOERR_XXX扩展错误代码。

sqlite3_backup_step()可能在以下情况返回SQLITE_READONLY

目标数据库以只读方式打开
目标数据库正在使用预先写日志的日志记录,并且目标和源页面大小不同
目标数据库是内存数据库,目标和源页面大小不同。

如果sqlite3_backup_step()无法获取所需的文件系统锁,那么调用繁忙处理程序函数(如果指定了一个)。如果处理繁忙程序在锁之前返回非零值,那么将SQLITE_BUSY返回给调用者。在这种情况下,可以稍后重试对sqlite3_backup_step()的调用。如果在调用sqlite3_backup_step()时正在使用源数据库连接写入源数据库,那么SQLITE_LOCKED会立即返回。同样,在这种情况下,可以稍后重试对sqlite3_backup_step()的调用。如果返回了SQLITE_IOERR_XXX,SQLITE_NOMEM或者SQLITE_READONLY,那么重新调用sqlite3_backup_step()就没有意义了。这些错误会被认为是致命的,应用程序必须接受备份操作失败,并将备份操作句柄传递给sqlite3_backup_finish()以释放相关资源。

对sqlite3_backup_step()的第一个调用获得了目标文件上的排它锁。排它锁只有在调用sqlite3_backup_finish()或者备份操作完成并且sqlite3_backup_step()返回SQLITE_DONE时才会释放。每次对sqlite3_backup_step()的调用都会获得一个在sqlite3_backup_step()调用期间持续的源数据库上的共享锁。因为源数据库在调用sqlite3_backup_step()之间没有被锁定,所以源数据库可能能在备份过程中被修改,如果源数据库被外部进程修改,或者通过与备份操作使用的数据库不同的数据库连接被修改,那么备份将会在下一个调用sqlite3_backup_step()时自动修改。如果使用与备份操作相同的数据库连接修改源数据库,则备份数据库将同时自动更新。

int sqlite3_backup_finish(sqlite3_backup *p);
p: 指向 sqlite3_backup 结构体的指针,这个结构体在之前通过 sqlite3_backup_init() 
函数初始化,用于备份操作。

当sqlite3_backup_step()返回SQLITE_DONE时,或者当应用程序希望放弃备份操作时,应用程序应该通过将sqlite3_backup_finish()来销毁sqlite3_backup。sqlite3_backup_finish()接口释放与sqlite3_backup对象关联的所有资源。如果sqlite3_backup_step()还没有返回SQLITE_DONE,那么目标数据库上任何活动的写事务都会回滚(如果在事务未完成之前发生了错误,或者事务被中断,回滚操作会撤销所有在该事务中进行的变更,恢复到事务开始前的状态。)。sqlite3_backup对象无效,不能在调用sqlite3_backup_finish()后使用。

如果没有sqlite3_backup_step()错误发生,sqlite3_backup_step()是否完成,sqlite3_backup_step()返回的值是SQLITE_OK。如果在同一个sqlite3_backup对象上的任何之前的sqlite3_backup_step()调用期间发生了内存不足情况或IO错误,那么sqlite3_backup_finish()将返回相应的错误代码。

注:sqlite3_backup_step()返回SQLITE_BUSY或SQLITE_LOCKED不是永久性错误,不会影响sqlite3_backup_finish()的返回值。

/*
** 该函数用于将数据库文件的内容加载到磁盘上
** 进入“主”数据库的开放数据库连接pInMemory,或
** 将pInMemory打开的数据库的当前内容保存到
** 磁盘上的数据库文件。pInMemory可能是一个内存数据库,
** 但如果不是,这个函数也会正常工作。
**
** 参数zFilename指向一个以空字符结尾的字符串,其中包含
** 磁盘上要加载或保存的数据库文件的名称。如果参数
** isSave为非零,则文件zFilename的内容为
** 用pInMemory打开的数据库内容覆盖。如果
** 参数isSave为0,则数据库打开的内容由
** pInMemory被从zFilename加载的数据所取代。
**
** 如果操作成功,则返回SQLITE_OK。否则,如果
** 发生错误,返回一个SQLite错误码。
*/
int loadOrSaveDb(sqlite3* pInMemory, const char* zFilename, int isSave) {int rc;                   /* 函数返回值 */sqlite3* pFile;           /* 在zFilename上打开数据库连接 */sqlite3_backup* pBackup;  /* 备份对象复制数据的对象 */sqlite3* pTo;             /* 要复制到的数据库(pFile或pInMemory) */sqlite3* pFrom;           /* 要从其中复制的数据库(pFile或pInMemory) *//* 打开以zFilename标识的数据库文件。如果由于任何原因失败,则提前退出。 */rc = sqlite3_open(zFilename, &pFile);if (rc == SQLITE_OK) {/* 如果这是一个` load `操作(isSave==0),则数据被复制** 从刚刚打开的数据库文件到数据库pInMemory。** 否则,如果这是一个` save `操作(isSave==1),则数据** 从pInMemory复制到pFile。设置变量pFrom和** 相应地pTo。 */pFrom = (isSave ? pInMemory : pFile);pTo = (isSave ? pFile : pInMemory);/* 设置备份程序,从“主”数据库复制** 将pFile连接到连接pInMemory的主数据库。** 如果出现错误,pBackup将被设置为NULL并报错** 连接pTo中的代码和消息。**** 如果成功创建了备份对象,调用backup_step()** 将数据从pFile复制到pInMemory。然后调用backup_finish()** 释放与pBackup对象关联的资源。如果一个** 发生错误,则会留下错误代码和消息** 连接pTo。如果没有发生错误,则错误代码属于** to pTo设置为SQLITE_OK。*/pBackup = sqlite3_backup_init(pTo, "main", pFrom, "main");if (pBackup) {(void)sqlite3_backup_step(pBackup, -1);(void)sqlite3_backup_finish(pBackup);}rc = sqlite3_errcode(pTo);}/* 关闭在数据库文件zFilename上打开的数据库连接** 并返回这个函数的结果 */(void)sqlite3_close(pFile);return rc;
}/*
** 在线备份数据库pDb到名为
** 通过zFilename。该函数将5个数据库页面从pDb复制到
** zFilename,然后解锁pDb并休眠250毫秒,然后重复执行
** 处理直到备份整个数据库。
**
** 传递给这个函数的第三个参数必须是一个指向进度的指针
** 函数。每组5页备份后,进度功能
** 调用时需要两个整数参数:剩余页数
** 复制,以及源文件中的总页数。这个信息
** 例如,可能用于更新GUI进度条。
**
** 当此函数运行时,另一个线程可能使用数据库pDb或
** 其他进程可以通过单独的连接。
**
** 如果备份过程成功完成,则返回SQLITE_OK。
** 否则,如果发生错误,则返回SQLite错误代码。
*/
int backupDb(sqlite3* pDb,               /* 数据库备份 */const char* zFilename,      /* 要备份的文件的名称 */void(*xProgress)(int, int)  /* 要调用的进度函数 */
) {int rc;                     /* 函数返回值 */sqlite3* pFile;             /* 在zFilename上打开数据库连接 */sqlite3_backup* pBackup;    /* 备份句柄用于复制数据 *//* 打开以zFilename标识的数据库文件。 */rc = sqlite3_open(zFilename, &pFile);if (rc == SQLITE_OK) {/* 打开用于完成传输的sqlite3_backup对象 */pBackup = sqlite3_backup_init(pFile, "main", pDb, "main");if (pBackup) {/* 这个循环的每次迭代都会从数据库复制5个数据库页面** pDb到备份数据库。如果backup_step()的返回值** pDb到备份数据库。如果backup_step()的返回值** 重复之前的250毫秒。 */do {rc = sqlite3_backup_step(pBackup, 5);xProgress(sqlite3_backup_remaining(pBackup),sqlite3_backup_pagecount(pBackup));if (rc == SQLITE_OK || rc == SQLITE_BUSY || rc == SQLITE_LOCKED) {sqlite3_sleep(250);}} while (rc == SQLITE_OK || rc == SQLITE_BUSY || rc == SQLITE_LOCKED);/* 释放由backup init()分配的资源。*/(void)sqlite3_backup_finish(pBackup);}rc = sqlite3_errcode(pFile);}/* 关闭在数据库文件zFilename上打开的数据库连接** 并返回这个函数的结果。 */(void)sqlite3_close(pFile);return rc;
}

此代码为SQLite官方样例代码。

这篇关于SQLite数据库(备份)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1133715

相关文章

JavaWeb项目创建、部署、连接数据库保姆级教程(tomcat)

《JavaWeb项目创建、部署、连接数据库保姆级教程(tomcat)》:本文主要介绍如何在IntelliJIDEA2020.1中创建和部署一个JavaWeb项目,包括创建项目、配置Tomcat服务... 目录简介:一、创建项目二、tomcat部署1、将tomcat解压在一个自己找得到路径2、在idea中添加

MySQL MHA集群详解(数据库高可用)

《MySQLMHA集群详解(数据库高可用)》MHA(MasterHighAvailability)是开源MySQL高可用管理工具,用于自动故障检测与转移,支持异步或半同步复制的MySQL主从架构,本... 目录mysql 高可用方案:MHA 详解与实战1. MHA 简介2. MHA 的组件组成(1)MHA

MySQL 数据库进阶之SQL 数据操作与子查询操作大全

《MySQL数据库进阶之SQL数据操作与子查询操作大全》本文详细介绍了SQL中的子查询、数据添加(INSERT)、数据修改(UPDATE)和数据删除(DELETE、TRUNCATE、DROP)操作... 目录一、子查询:嵌套在查询中的查询1.1 子查询的基本语法1.2 子查询的实战示例二、数据添加:INSE

通过DBeaver连接GaussDB数据库的实战案例

《通过DBeaver连接GaussDB数据库的实战案例》DBeaver是一个通用的数据库客户端,可以通过配置不同驱动连接各种不同的数据库,:本文主要介绍通过DBeaver连接GaussDB数据库的... 目录​一、前置条件​二、连接步骤​三、常见问题与解决方案​1. 驱动未找到​2. 连接超时​3. 权限不

MySQL数据库读写分离与负载均衡的实现逻辑

《MySQL数据库读写分离与负载均衡的实现逻辑》读写分离与负载均衡是数据库优化的关键策略,读写分离的核心是将数据库的读操作与写操作分离,本文给大家介绍MySQL数据库读写分离与负载均衡的实现方式,感兴... 目录读写分离与负载均衡的核心概念与目的读写分离的必要性与实现逻辑读写分离的实现方式及优缺点读负载均衡

Go语言中如何进行数据库查询操作

《Go语言中如何进行数据库查询操作》在Go语言中,与数据库交互通常通过使用数据库驱动来实现,Go语言支持多种数据库,如MySQL、PostgreSQL、SQLite等,每种数据库都有其对应的官方或第三... 查询函数QueryRow和Query详细对比特性QueryRowQuery返回值数量1个:*sql

Mysql数据库聚簇索引与非聚簇索引举例详解

《Mysql数据库聚簇索引与非聚簇索引举例详解》在MySQL中聚簇索引和非聚簇索引是两种常见的索引结构,它们的主要区别在于数据的存储方式和索引的组织方式,:本文主要介绍Mysql数据库聚簇索引与非... 目录前言一、核心概念与本质区别二、聚簇索引(Clustered Index)1. 实现原理(以 Inno

sqlserver、mysql、oracle、pgsql、sqlite五大关系数据库的对象名称和转义字符

《sqlserver、mysql、oracle、pgsql、sqlite五大关系数据库的对象名称和转义字符》:本文主要介绍sqlserver、mysql、oracle、pgsql、sqlite五大... 目录一、转义符1.1 oracle1.2 sqlserver1.3 PostgreSQL1.4 SQLi

MySQL数据库双机热备的配置方法详解

《MySQL数据库双机热备的配置方法详解》在企业级应用中,数据库的高可用性和数据的安全性是至关重要的,MySQL作为最流行的开源关系型数据库管理系统之一,提供了多种方式来实现高可用性,其中双机热备(M... 目录1. 环境准备1.1 安装mysql1.2 配置MySQL1.2.1 主服务器配置1.2.2 从

SpringBoot基于注解实现数据库字段回填的完整方案

《SpringBoot基于注解实现数据库字段回填的完整方案》这篇文章主要为大家详细介绍了SpringBoot如何基于注解实现数据库字段回填的相关方法,文中的示例代码讲解详细,感兴趣的小伙伴可以了解... 目录数据库表pom.XMLRelationFieldRelationFieldMapping基础的一些代