SQLite学习手册(临时文件)

1、简介:

    尽管SQLite的数据库是由单一文件构成,然而事实上在SQLite运行时却存在着一些隐含的临时文件,这些临时文件是出于不一样的目的而存在的,对于开发者而言,它们是透明的,所以在开发的过程当中咱们并不须要关注它们的存在。尽管如此,若是能对这些临时文件的产生机制和应用场景有着很好的理解,那么对咱们从此应用程序的优化和维护都是极有帮助的。在SQLite中主要产生如下七种临时文件,如:
    1). 回滚日志。
    2). 主数据库日志。
    3). SQL语句日志。
    4). 临时数据库文件。
    5). 视图和子查询的临时持久化文件。
    6). 临时索引文件。
    7). VACUUM命令使用的临时数据库文件。
    
2、具体说明:

     1. 回滚日志:
    SQLite为了保证事物的原子性提交和回滚,在事物开始时建立了该临时文件。此文件始终位于和数据库文件相同的目录下,其文件名格式为:  数据库文件名 + "-journal" 。换句话说,若是没有该临时文件的存在,当程序运行的系统出现任何故障时,SQLite将没法保证事物的完整性,以及数据状态的一致性。该文件在事物提交或回滚后将被马上删除。
    在事物运行期间,若是当前主机因电源故障而宕机,而此时因为回滚日志文件已经保存在磁盘上,那么当下一次程序启动时,SQLite在打开数据库文件的过程当中将会发现该临时文件的存在,咱们称这种日志文件为"Hot Journal"。SQLite会在成功打开数据库以前先基于该文件完成数据库的恢复工做,以保证数据库的数据回复到上一个事物开始以前的状态。
    在SQLite中咱们能够经过修改 journal_mode pragma ,而使SQLite对维护该文件采用不一样的策略。缺省状况下该值为 DELETE ,即在事物结束后删除日志文件。而PERSIST选项值将不会删除日志文件,而是将回滚日志文件的头部清零,从而避免了文件删除所带的磁盘开销。再有就是OFF选项值,该值将指示SQLite在开始事物时不产生回滚日志文件,这样一旦出现系统故障,SQLite也没法再保障数据库数据的一致性。

    2. 主数据库日志:
    在SQLite中,若是事物的操做做用于多个数据库,即经过ATTACH命令附加到当前链接中的数据库,那么SQLite将生成主数据库日志文件以保证事物产生的改变在多个数据库之间保持原子性。和回滚日志文件同样,主数据库日志文件也位于当前链接中主数据库文件所处的目录内,其文件名格式为:主数据库文件名 + 随机的后缀。在该文件中,将包含全部当前事物将会改变的Attached数据库的名字。在事物被提交以后,此文件亦被SQLite随之删除。
    主数据库日志文件只有在某一事物同时操做多个数据库时(主数据库和Attached数据库)才有可能被建立。经过该文件,SQLite能够实现跨多个数据库的事物原子性,不然,只能简单的保证每一个单一的数据库内的状态一致性。换句话说,若是该事物在执行的过程当中出现系统崩溃或主机宕机的现象,在进行数据恢复时,若没有该文件的存在,将会致使部分SQLite数据库处于提交状态,而另一部分则处于回滚状态,所以该事物的一致性将被打破。

     3. SQL语句日志:
    在一个较大的事物中,SQLite为了保证部分数据在出现错误时能够被正常回滚,因此在事物开始时建立了SQL语句日志文件。好比,update语句修改了前50条数据,然而在修改第51条数据时发现该操做将会破坏某字段的惟一性约束,最终SQLite将不得不经过该日志文件回滚已经修改的前50条数据。
    SQL语句日志文件只有在INSERT或UPDATE语句修改多行记录时才有可能被建立,与此同时,这些操做还极有可能会打破某些约束并引起异常。可是若是INSERT或UPDATE语句没有被包含在BEGIN...COMMIT中,同时也没有任何其它的SQL语句正在当前的链接上运行,在这种状况下,SQLite将不会建立SQL语句日志文件,而是简单的经过回滚日志来完成部分数据的UNDO操做。
    和上面两种临时文件不一样的是,SQL语句日志文件并不必定要存储在和数据库文件相同的目录下,其文件名也是随机生成。该文件所占用的磁盘空间须要视UPDATE或INSERT语句将要修改的记录数量而定。在事物结束后,该文件将被自动删除。

     4. 临时数据库文件:
    当使用"CREATE TEMP TABLE"语法建立临时数据表时,该数据表仅在当前链接内可见,在当前链接被关闭后,临时表也随之消失。然而在生命期内,临时表将连同其相关的索引和视图均会被存储在一个临时的数据库文件以内。该临时文件是在第一次执行"CREATE TEMP TABLE"时即被建立的,在当前链接被关闭后,该文件亦将被自动删除。最后须要说明的是,临时数据库不能被执行DETACH命令,同时也不能被其它进程执行ATTACH命令。
    
     5. 视图和子查询的临时持久化文件:
    在不少包含子查询的查询中,SQLite的执行器会将该查询语句拆分为多个独立的SQL语句,同时将子查询的结果持久化到临时文件中,以后在基于该临时文件中的数据与外部查询进行关联,所以咱们能够称其为物化子查询。一般而言,SQLite的优化器会尽力避免子查询的物化行为,可是在有些时候该操做是没法避免的。该临时文件所占用的磁盘空间须要依赖子查询检索出的数据数量,在查询结束后,该文件将被自动删除。见以下示例:
     SELECT * FROM ex1 WHERE ex1.a IN (SELECT b FROM ex2);
    在上面的查询语句中,子查询SELECT b FROM ex2的结果将会被持久化到临时文件中,外部查询在运行时将会为每一条记录去检查该临时文件,以判断当前记录是否出如今临时文件中,若是是则输出当前记录。显而易见的是,以上的行为将会产生大量的IO操做,从而显著的下降了查询的执行效率,为了不临时文件的生成,咱们能够将上面的查询语句改成:
     SELECT * FROM ex1 WHERE EXISTS(SELECT 1 FROM ex2 WHERE ex2.b=ex1.a);
    对于以下查询语句,若是SQLite不作任何智能的rewrite操做,该查询中的子查询也将会被持久化到临时文件中,如:
     SELECT * FROM ex1 JOIN (SELECT b FROM ex2) AS t ON t.b=ex1.a;
    在SQLite自动将其修改成下面的写法后,将不会再生成临时文件了,如:
     SELECT ex1.*, ex2.b FROM ex1 JOIN ex2 ON ex2.b=ex1.a;

     6. 临时索引文件:
    当查询语句包含如下SQL从句时,SQLite为存储中间结果而建立了临时索引文件,如:
    1). ORDER BY或GROUP BY从句。
    2). 汇集查询中的DISTINCT关键字。
    3). 由UNION、EXCEPT和INTERSECT链接的多个SELECT查询语句。
    须要说明的是,若是在指定的字段上已经存在了索引,那么SQLite将不会再建立该临时索引文件,而是经过直接遍历索引来访问数据并提取有用信息。若是没有索引,则须要将排序的结果存储在临时索引文件中以供后用。该临时文件所占用的磁盘空间须要依赖排序数据的数量,在查询结束后,该文件被自动删除。

     7. VACUUM命令使用的临时数据库文件:
    VACUUM命令在工做时将会先建立一个临时文件,而后再将重建的整个数据库写入到该临时文件中。以后再将临时文件中的内容拷贝回原有的数据库文件中,最后删除该临时文件。
    该临时文件所占用的磁盘空间不会超过原有文件的尺寸。

3、相关的编译时参数和指令:

    对于SQLite来讲,回滚日志、主数据库日志和SQL语句日志文件在须要的时候SQLite都会将它们写入磁盘文件,可是对于其它类型的临时文件,SQLite是能够将它们存放在内存中以取代磁盘文件的,这样在执行的过程当中就能够减小大量的IO操做了。要完成该优化主要依赖于如下三个因素:
     1. 编译时参数SQLITE_TEMP_STORE:
    该参数是源代码中的宏定义(#define),其取值范围是0到3(缺省值为1),见以下说明:
    1). 等于0时,临时文件老是存储在磁盘上,而不会考虑temp_store pragma指令的设置。
    2). 等于1时,临时文件缺省存储在磁盘上,可是该值能够被temp_store pragma指令覆盖。
    3). 等于2时,临时文件缺省存储在内存中,可是该值能够被temp_store pragma指令覆盖。
    4). 等于3时,临时文件老是存储在内存中,而不会考虑temp_store pragma指令的设置。
    
     2. 运行时指令temp_store pragma:
    该指令的取值范围是0到2(缺省值为0),在程序运行时该指令能够被动态的设置,见以下说明:
    1). 等于0时,临时文件的存储行为彻底由SQLITE_TEMP_STORE编译期参数肯定。
    2). 等于1时,若是编译期参数SQLITE_TEMP_STORE指定使用内存存储临时文件,那么该指令将覆盖这一行为,使用磁盘存储。
    2). 等于2时,若是编译期参数SQLITE_TEMP_STORE指定使用磁盘存储临时文件,那么该指令将覆盖这一行为,使用内存存储。
    
     3. 临时文件的大小:
    对于以上两个参数,都有参数值表示缺省状况是存储在内存中的,只有当临时文件的大小超过必定的阈值后才会根据必定的算法,将部分数据写入到磁盘中,以避免临时文件占用过多的内存而影响其它程序的执行效率。
    
    最后在从新赘述一遍,SQLITE_TEMP_STORE编译期参数和temp_store pragma运行时指令只会影响除回滚日志和主数据库日志以外的其它临时文件的存储策略。换句话说,回滚日志和主数据库日志将老是将数据写入磁盘,而不会关注以上两个参数的值。

4、其它优化策略:

    在SQLite中因为采用了Page Cache的缓冲优化机制,所以即使临时文件被指定存储在磁盘上,也只有当该文件的大小增加到必定的尺寸后才有可能被SQLite刷新到磁盘文件上,在此以前它们仍将驻留在内存中。这就意味着对于大多数场景,若是临时表和临时索引的数据量相对较少,那么它们是不会被写到磁盘中的,固然也就不会有IO事件发生。只有当它们增加到内存不能容纳的时候才会被刷新到磁盘文件中的。其中SQLITE_DEFAULT_TEMP_CACHE_SIZE编译期参数能够用于指定临时表和索引在占用多少Cache Page时才须要被刷新到磁盘文件,该参数的缺省值为500页。
相关文章
相关标签/搜索