小。速度很快。可靠。
选择任意三个选项。
RBU扩展

1RBU扩展

RBU扩展是SQLite的一个附加组件,设计用于大型网络边缘低功耗设备上的SQLite数据库文件。RBU可用于两个单独的任务:

首字母缩写RBU代表“可恢复批量更新”。

这两个RBU功能都可以使用SQLite的内置SQL命令-通过一系列的RBU更新插入,删除更新单个事务中的命令和单个事务的RBU真空真空命令。RBU模块具有以下优点这些简单的方法:

  1. RBU可能更高效

    将更改应用于B-Tree(数据结构)的最有效方法SQLite用于将每个表和索引存储在磁盘上)是为了更改键顺序。但如果SQL表有一个或多个索引,则键每个索引的顺序可能与主表和其他索引的顺序不同辅助指标。因此,在执行一系列插入,更新删除语句,通常不可能对操作,使得所有b树都按密钥顺序进行更新。RBU更新过程通过将所有更改应用于一个主表来解决此问题通过,然后在单独的过程中将更改应用于每个索引,确保每个优化更新B-Tree。对于大型数据库文件(不适合操作系统磁盘缓存)此过程可以导致以下两个顺序更新速度更快。

    RBU真空操作需要较少的临时磁盘空间和写入到磁盘的数据比SQLite VACUUM少。SQLite VACUUM大约需要要运行的临时磁盘空间中的最终数据库文件的两倍大小。写入的数据总量大约是最终数据库文件。相比之下,RBU真空需要大致的尺寸在临时磁盘空间中写入最终数据库文件,总共写入两倍于磁盘。

    另一方面,RBU真空吸尘器比普通SQLite使用更多的CPU真空-在一次测试中,真空度是真空度的五倍。因此,RBU在相同条件下,真空通常比SQLite真空慢得多条件。

  2. RBU在后台运行

    正在进行的RBU操作(更新或真空)不会干扰对数据库文件的读取访问。

  3. RBU递增运行

    RBU操作可能会暂停,然后恢复,可能是干预性停电和/或系统重置。对于RBU更新原始数据库内容对所有数据库阅读器保持可见,直到已应用整个更新-即使更新被挂起并且后来又恢复了。

默认情况下未启用RBU扩展。要启用它,请编译合并使用SQLITE_ENABLE_RBU数据库compile-time选项。

2RBU更新

2.1.RBU更新限制

以下限制适用于RBU更新:

2.2.准备RBU更新文件

RBU应用的所有更改都存储在单独的SQLite数据库中称为“RBU数据库”。要修改的数据库称为“目标数据库”。

对于目标数据库中将被更新修改的每个表,在RBU数据库中创建相应的表。RBU数据库表模式与目标数据库的模式不同,但它是派生的从它作为如下所述.

RBU数据库表包含每个目标数据库的一行通过更新插入、更新或删除行。填充RBU数据库表在中进行了描述以下部分.

2.2.1、。RBU数据库模式

对于目标数据库中的每个表,RBU数据库应包含一个表命名的”数据<整数>_<目标表名称>“其中<目标表名称>是目标中表的名称数据库和<整数>是零或多个数字的任意序列字符(0-9)。RBU数据库中的表按顺序由名称(根据BINARY排序顺序从最小到最大),因此,目标表的更新顺序受选择的影响的<整数>data_%表名的一部分。虽然这可以使用RBU更新时非常有用某些类型的虚拟表,通常没有使用空字符串以外的任何内容来代替的原因<整数>.

data_%表必须与目标表具有相同的所有列,加上另一列名为“rbucontrol”。data_%表应该没有PRIMARY KEY或UNIQUE约束,但每列的类型应与目标数据库中的相应列。rbu_control列应该根本没有类型。例如,如果目标数据库包含:

创建表t1(a整数主键,b文本,c唯一);

然后RBU数据库应包含:

创建表格data_t1(a整数,b文本,c,rbu_control);

data_%表中列的顺序无关紧要。

如果目标数据库表是虚拟表或没有PRIMARY KEY声明,data_%表还必须包含一列名为“rbu_rowid”。rbu_rowid列映射到表粗鲁的.例如,如果目标数据库包含以下内容之一:

使用fts3(a,b)创建虚拟表x1;创建表x1(a,b);

则RBU数据库应包含:

创建表格数据_x1(a,b,rbu_rowid,rbu_control);

“rowid”列对其执行操作的虚拟表not的功能类似于无法使用RBU更新主键值。

的所有非隐藏列(即“SELECT*”匹配的所有列)输入表中必须存在目标表。对于虚拟表,隐藏列是可选的-如果在中存在,则由RBU更新输入表,否则不显示。例如,写入fts4带有隐藏languageid列的表,例如:

使用fts4(a,b,languageid='langid')创建虚拟表ft1;

可以使用以下任一输入表模式:

CREATE TABLE data_ft1(a、b、langid、rbu_rowid、rbu_control);创建表格data_ft1(a,b,rbu_rowid,rbu_control);

2.2.2.RBU数据库内容

对于要作为RBU的一部分插入到目标数据库中的每一行更新时,相应的data_%表应包含单个记录将“rbucontrol”列设置为包含整数值0。这个其他列应设置为构成新记录的值插入。

“rbu_control”列也可以设置为整数值2插入。在这种情况下,新行以静默方式替换任何现有行具有相同的主键值。这相当于DELETE后跟一个使用相同的主键值INSERT。它与SQL REPLACE不同命令,因为在这种情况下,新行可以替换任何冲突的行(即。那些由于UNIQUE约束或索引而冲突的),而不仅仅是那些主键冲突。

如果目标数据库表具有INTEGER PRIMARY KEY,则不是可以在IPK列中插入NULL值。正在尝试这样会导致SQLITE_MISMATCH错误。

对于要作为RBU的一部分从目标数据库中删除的每一行更新,相应的data_%表应包含一条记录将“rbucontrol”列设置为包含整数值1。这个要删除的行的实际主键值应存储在data_%表的相应列。存储在不使用其他列。

对于要作为RBU的一部分从目标数据库更新的每一行更新时,相应的data_%表应包含单个记录将“rbucontrol”列设置为包含文本类型的值。标识要更新的行的真正主键值应该是存储在data_%表行的相应列中正在更新的所有列的新值。中的文本值“rbu_control”列必须包含与相同数量的字符目标数据库表中有列,并且必须完全包含“x”和“.”字符(或在某些特殊情况下为“d”-见下文)。对于每个正在更新的列,相应的字符设置为“x”。对于保持原状的对象rbu_control值应设置为“.”。例如,给定表格上面的更新语句:

更新t1集合c='usa',其中a=4;

由以下创建的data_t1行表示:

插入data_t1(a,b,c,rbu_control)值(4,NULL,'usa','..x');

如果使用RBU更新目标数据库中的大BLOB值可以更有效地存储可用于修改的补丁或增量现有BLOB,而不是RBU数据库中的全新值。RBU允许以两种方式指定增量:

化石增量格式只能用于更新BLOB值。相反通过将新的BLOB存储在data_%表中,化石delta被存储而不是。而不是将“x”指定为rbu_control字符串的一部分对于要更新的列,将存储一个“f”字符。处理时“f”更新,RBU从磁盘加载原始BLOB数据,应用化石增量并将结果存储回数据库文件。RBU数据库由生成sqldiff—rbu利用化石三角洲这样做可以节省RBU数据库中的空间。

要使用自定义增量格式,RBU应用程序必须注册一个开始处理更新。将使用两个参数调用rbu_delta()-原始值存储在目标表列中,并将增量值作为RBU更新。它应该返回将增量应用于原始价值。要使用自定义增量函数与要更新的目标列对应的rbu_control值必须为设置为“d”而不是“x”。然后,使用值存储在相应的data_%列中,RBU调用用户定义的SQL函数“rbu_delta()”和目标表列中的存储。

例如,此行:

插入data_t1(a,b,c,rbu_control)值(4,NULL,'usa','..d');

导致RBU更新目标数据库表的方式类似于:

更新t1集合c=rbu_delta(c,'usa'),其中a=4;

如果目标数据库表是虚拟表或没有PRIMARY的表KEY,rbu_control值不应包含对应的字符到rburowid值。例如:

插入data_ft1(a,b,rbu_rowid,rbu_control)值(NULL,'usa',12,'.x');

导致类似以下结果:

更新ft1集合b='usa',其中rowid=12;

data_%表本身应该没有PRIMARY KEY声明。然而,如果从每个数据中读取行_%,RBU的效率会更高按“rowid”顺序排列的表与读取它们的顺序大致相同相应目标数据库表的PRIMARY KEY。在其他单词,行应使用目标表PRIMARY KEY进行排序字段插入data_%表之前。

2.2.3.使用带FTS3/4表的RBU

通常FTS3或FTS4表是虚拟表的一个示例具有类似于主键的rowid。因此,对于以下FTS4表:

使用fts4(addr,text)创建虚拟表格ft1;使用fts4创建虚拟表ft2;--隐式“内容”列

data_%表可以创建如下:

使用fts4(addr、text、rbu_rowid、rbu_control)创建表格data_ft1;使用fts4创建表data_ft2(内容,rbu_rowid,rbu_control);

并进行填充,就像目标表是没有显式PRIMARY KEY列。

无争议的FTS4表格处理方式类似,除非在以下情况下,任何更新或删除行的尝试都会导致错误应用更新。

外部内容FTS4表也可能是使用RBU更新。在这种情况下,用户需要配置RBU数据库,以便相同的UPDATE、DELETE和INSERT操作集应用于FTS4索引和基础内容表。至于所有人更新外部内容FTS4表,用户还需要确保之前对FTS4索引应用任何UPDATE或DELETE操作它们应用于基础内容表(请参阅FTS4文档详细说明)。在RBU中,通过确保名称用于写入FTS4表的data_%表的排序在名称之前用于使用二进制的排序序列。为了避免在RBU数据库,可以使用SQL视图来代替其中一个data_%表。例如,对于目标数据库模式:

创建表格ccc(地址,文本);使用fts4(addr,text,content=ccc)创建虚拟表ccc_fts;

可以使用以下RBU数据库模式:

创建表格data_ccc(addr,text,rbu_rowid,rbu_control);将视图data0_ccc_fts创建为SELECT*FROM data_ccc;

然后可以使用预期的更新正常填充data_ccc表用于目标数据库表ccc。RBU将从中读取相同的更新data0_ccc_fts视图并应用于fts表ccc_fts。因为“data0_ccc_fts”小于“data_ccc”,将更新fts表首先,根据需要。

底层内容表具有显式INTEGER PRIMARY的情况KEY列稍微困难一些,因为存储在rbu_control列与FTS索引及其基础内容表。对于基础内容表,字符必须包含在显式IPK的任何rbu_control文本值中,但对于具有隐式rowid的FTS表本身,它不应该这样做。这个不方便,但可以使用更复杂的视图解决,如下所示:

--目标数据库架构创建表ddd(i整数主键,k文本);使用fts4(k,content=ddd)创建虚拟表ddd_fts;--RBU数据库模式创建表格data_ccc(i,k,rbu_control);将视图数据0_ccc_fts创建为SELECT i AS rbu_rowid,k,CASE当rbu_control IN(0,1)时,rbu_ccontrol ELSE substr(rbu_conrol,2)END来自data_ccc;

上述SQL视图中的substr()函数返回带有第一个字符的rbu_control参数(对应于删除FTS表中不需要的“i”列)。

2.2.4.使用sqldiff自动生成RBU更新

截至SQLite版本3.9.0(2015-10-14), 这个SQL差异实用程序能够生成RBU数据库表示具有相同的模式。例如,以下命令:

sqldiff—rbu t1.db t2.db

输出SQL脚本以创建RBU数据库,如果用于更新数据库t1.db,对其进行修补,使其内容与数据库t2.db。

默认情况下,sqldiff会尝试处理提供给它的两个数据库。如果一个数据库中出现任何表但不是另一个,或者如果任何表中的模式略有不同一个数据库它是一个错误。如果出现以下情况,“--table”选项可能有用导致问题

sqldiff默认忽略虚拟表。然而,这是可能的为具有以下特性的虚拟表显式创建RBU data_%表一个rowid,其功能类似于使用以下命令的主键:

sqldiff--rbu--表<虚拟表格名称>t1.db t2.db(t1.db)

不幸的是,即使默认情况下忽略虚拟表基础数据库表他们为了数据库中的存储数据不是,并且SQL差异将包括添加这些任何RBU数据库。因此,用户尝试使用sqldiff创建RBU更新以应用于具有一个或多个虚拟数据库的目标数据库表可能必须使用--table选项单独运行sqldiff在目标数据库中更新每个表。

2.3.RBU更新C/C++编程

RBU扩展接口允许应用程序应用RBU更新将存储在RBU数据库中的数据转换为现有目标数据库。程序如下:

  1. 使用sqlite3rbu_Open(T,A,S)函数打开RBU句柄。

    T参数是目标数据库文件的名称。A参数是RBU数据库文件的名称。S参数是用于存储的“状态数据库”的名称中断后恢复更新所需的状态信息。S参数可以为NULL,在这种情况下,状态信息存储在RBU数据库中的各个表中,这些表的名称都是以“rbu”开头。

    sqlite3rbu_open(T,A,S)函数返回指向“sqlite3rbu”对象,然后将其传递给后续对象接口。

  2. 向数据库注册任何必需的虚拟表模块sqlite3rbu_db(X)返回的句柄(其中参数X是sqlite3rdbu从sqlite3rbu_open()返回的指针)。此外,如果需要,请注册rbu_delta()SQL函数使用sqlite3_create_function_v2().

  3. 在上调用sqlite3rbu_step(X)函数一次或多次sqlite3rbu对象指针X。每次调用sqlite3rbu _step()执行单个b-tree操作,因此可能会有数千个调用需要应用完整更新。sqlite3rbu_step()更新完成后,接口将返回SQLITE_DONE完全应用。

  4. 调用sqlite3rbu_close(X)销毁sqlite3rdbu对象指针。如果调用sqlite3rbu_step(X)的次数足够多将更新应用于目标数据库,然后应用于RBU数据库标记为完全应用。否则,RBU的状态更新应用程序保存在状态数据库中(或RBU中数据库,如果sqlite3rbu_open()中的状态数据库文件的名称为NULL),以便以后恢复更新。

如果更新仅由调用sqlite3rbu_close()时,保存状态信息在状态数据库中(如果存在),或在RBU数据库中。这允许后续流程自动从停止的位置恢复RBU更新。如果状态信息存储在RBU数据库中,则可以将其删除删除名称以“rbu”开头的所有表。

有关更多详细信息,请参阅中的注释头文件平方米3rbu。小时.

三。RBU真空

3.1.RBU真空限制

与SQLite的内置VACUUM命令相比,RBU VACUUM具有以下限制:

3.2.RBU真空C/C++编程

本节概述了演示将RBU真空集成到应用程序中。有关详细信息,请参阅中的注释头文件平方米3rbu。小时.

RBU真空应用都实现了以下一些变化程序:

  1. 通过调用sqlite3rbu_vacution(T,S)创建RBU句柄。

    参数T是要清空的数据库文件的名称。参数S为如果真空操作暂停。

    如果sqlite3rbu_vacuum()为调用时,它将自动创建并填充单个表用于存储RBU真空的状态-“RBU_state”。如果正在进行RBU真空暂停,此表中填充了状态数据。下一个使用相同的S参数调用sqlite3rbu_vacuum()时,它会检测此数据并尝试恢复暂停的真空操作。什么时候?RBU真空操作完成或遇到错误,RBU自动删除rbustate表的内容。在这种情况下,下一次调用sqlite3rbuvacuum()将启动一个全新的真空从头开始操作。

    建立一个确定RBU的公约是一个好主意基于目标数据库名称的真空状态数据库名称。这个下面的示例代码使用“<target>-真空”,其中<target>为正在清空的数据库的名称。

  2. 数据库中索引使用的任何自定义排序规则序列使用返回的两个数据库句柄进行注册通过sqlite3rbu_db()函数。

  3. 在RBU句柄上调用函数sqlite3rbu_step(),直到RBU真空完成,出现错误或应用程序希望暂停RBU真空。

    每次调用sqlite3rbu_step()都会对完成真空操作。根据数据库的大小单个真空可能需要数千次调用sqlite3rbustep()。如果真空操作具有如果真空操作尚未完成但没有错误,则返回SQLITE_OK发生,如果遇到错误,则返回SQLite错误代码。如果如果发生错误,则立即调用sqlite3rbu_step()返回相同的错误代码。

  4. 最后,调用sqlite3rbu_close()来关闭RBU句柄。如果应用程序在真空之前停止调用sqlite3rbu_step()完成或发生错误时,真空状态保存在状态数据库,以便稍后恢复。

    与sqlite3rbu_step()类似,如果真空操作已完成,sqlite3rbu_close()返回SQLITE_DONE。如果真空尚未完成但没有发生错误,返回SQLITE_OK。或者,如果出现错误发生时,返回SQLite错误代码。如果作为一部分发生错误在之前调用sqlite3rbu_step()的过程中,sqlite3rbu_close()返回相同的错误代码。

下面的示例代码说明了上述技术。

/*
**启动新RBU真空或恢复暂停的RBU真空
**数据库zTarget。当任一错误发生时返回RBU
**真空完成或应用程序发出中断信号时
**(代码未显示)。
**
**如果RBU真空成功完成,则返回SQLITE_DONE。
**如果发生错误,则返回SQLite错误代码。或者,如果应用程序
**发出中断信号,暂停RBU真空操作,以便
**可以通过对此函数的后续调用恢复,并返回
**SQLITE_OK。
**
**此函数使用名为“<zTarget>-dvacution”的数据库
**状态数据库,其中<zTarget>是数据库的名称
**正在吸尘。
*/int do_rbu_vacuum(const char*zTarget){整数rc;char*zState;/*状态数据库名称*/sqlite3rbu*pRbu;/*RBU真空手柄*/zState=sqlite3_mprintf(“%s真空”,zTarget);如果(zState==0)返回SQLITE_NOMEM;pRbu=sqlite3rbu_vacuum(zTarget,zState);sqlite3_free(zState);如果(pRbu){sqlite3*dbTarget=sqlite3rbu_db(pRbu,0);sqlite3*dbState=sqlite3rbu_db(pRbu,1);/*目标数据库使用的任何自定义排序规则序列都必须
    **在这里注册两个数据库句柄*/while(sqlite3rbu_step(pRbu)==SQLITE_OK){如果(<应用程序已发出中断信号>)断裂;}}rc=sqlite3rbu_close(pRbu);返回rc;}