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

相关文章

Spring Security基于数据库验证流程详解

Spring Security 校验流程图 相关解释说明(认真看哦) AbstractAuthenticationProcessingFilter 抽象类 /*** 调用 #requiresAuthentication(HttpServletRequest, HttpServletResponse) 决定是否需要进行验证操作。* 如果需要验证,则会调用 #attemptAuthentica

MySQL数据库宕机,启动不起来,教你一招搞定!

作者介绍:老苏,10余年DBA工作运维经验,擅长Oracle、MySQL、PG、Mongodb数据库运维(如安装迁移,性能优化、故障应急处理等)公众号:老苏畅谈运维欢迎关注本人公众号,更多精彩与您分享。 MySQL数据库宕机,数据页损坏问题,启动不起来,该如何排查和解决,本文将为你说明具体的排查过程。 查看MySQL error日志 查看 MySQL error日志,排查哪个表(表空间

深入理解数据库的 4NF:多值依赖与消除数据异常

在数据库设计中, "范式" 是一个常常被提到的重要概念。许多初学者在学习数据库设计时,经常听到第一范式(1NF)、第二范式(2NF)、第三范式(3NF)以及 BCNF(Boyce-Codd范式)。这些范式都旨在通过消除数据冗余和异常来优化数据库结构。然而,当我们谈到 4NF(第四范式)时,事情变得更加复杂。本文将带你深入了解 多值依赖 和 4NF,帮助你在数据库设计中消除更高级别的异常。 什么是

DM8数据库安装后配置

1 前言 在上篇文章中,我们已经成功将库装好。在安装完成后,为了能够更好地满足应用需求和保障系统的安全稳定运行,通常需要进行一些基本的配置。下面是一些常见的配置项: 数据库服务注册:默认包含14个功能模块,将这些模块注册成服务后,可以更好的启动和管理这些功能;基本的实例参数配置:契合应用场景和发挥系统的最大性能;备份:有备无患;… 2 注册实例服务 注册了实例服务后,可以使用系统服务管理,

速了解MySQL 数据库不同存储引擎

快速了解MySQL 数据库不同存储引擎 MySQL 提供了多种存储引擎,每种存储引擎都有其特定的特性和适用场景。了解这些存储引擎的特性,有助于在设计数据库时做出合理的选择。以下是 MySQL 中几种常用存储引擎的详细介绍。 1. InnoDB 特点: 事务支持:InnoDB 是一个支持 ACID(原子性、一致性、隔离性、持久性)事务的存储引擎。行级锁:使用行级锁来提高并发性,减少锁竞争

开源分布式数据库中间件

转自:https://www.csdn.net/article/2015-07-16/2825228 MyCat:开源分布式数据库中间件 为什么需要MyCat? 虽然云计算时代,传统数据库存在着先天性的弊端,但是NoSQL数据库又无法将其替代。如果传统数据易于扩展,可切分,就可以避免单机(单库)的性能缺陷。 MyCat的目标就是:低成本地将现有的单机数据库和应用平滑迁移到“云”端

ORACLE 11g 创建数据库时 Enterprise Manager配置失败的解决办法 无法打开OEM的解决办法

在win7 64位系统下安装oracle11g,在使用Database configuration Assistant创建数据库时,在创建到85%的时候报错,错误如下: 解决办法: 在listener.ora中增加对BlueAeri-PC或ip地址的侦听,具体步骤如下: 1.启动Net Manager,在“监听程序”--Listener下添加一个地址,主机名写计

MyBatis 切换不同的类型数据库方案

下属案例例当前结合SpringBoot 配置进行讲解。 背景: 实现一个工程里面在部署阶段支持切换不同类型数据库支持。 方案一 数据源配置 关键代码(是什么数据库,该怎么配就怎么配) spring:datasource:name: test# 使用druid数据源type: com.alibaba.druid.pool.DruidDataSource# @需要修改 数据库连接及驱动u

CentOS下mysql数据库data目录迁移

https://my.oschina.net/u/873762/blog/180388        公司新上线一个资讯网站,独立主机,raid5,lamp架构。由于资讯网是面向小行业,初步估计一两年内访问量压力不大,故,在做服务器系统搭建的时候,只是简单分出一个独立的data区作为数据库和网站程序的专区,其他按照linux的默认分区。apache,mysql,php均使用yum安装(也尝试

Java基础回顾系列-第九天-数据库编程

Java基础回顾系列-第九天-数据库编程 数据库简介工具包java.sql API 内容与数据库建立连接执行SQL语句数据库检索和更新查询结果SQL类型对应Java类型映射元数据异常 API方法DriverManagerConnectionStatementPreparedStatementCallableStatementResultSetjava.sql.Date批处理、存储过程、事务