Sqlite学习笔记(三)&&WAL性能测试中列出了几种典型场景下WAL的性能数据,了解到WAL确实有性能优势,这篇文章将会详细分析WAL的原理,做到知其然,更要知其所以然。

WAL是什么

WAL(Write ahead logging)是一种日志模式,它是一种思想,普遍应用于关系型数据库。每个事务执行变更时,修改数据页,同时会产生日志,这样在事务提交后,不需要将修改的脏页刷盘,只需要将事务产生的日志落盘即可返回。WAL保证日志一定先于对应的脏页落盘,就是所谓的WAL。SQLITE在3.7版本以后引入WAL,它的WAL也基本采用这个原理,只不过SQLite实现比较简单,日志记录的是修改后的页,而不是所谓的修改日志。WAL模式下,SQlite中除了db文件,还包含了两个文件,.wal文件和.shm文件,前者是日志文件,后者是日志索引文件。

日志模式

SQLite中日志模式主要有DELETE和WAL两种,其他几种比如TRUNCATE,PERSIST,MEMORY基本原理都与DELETE模式相同,不作详细展开。DELETE模式下,日志中记录的变更前数据页内容;WAL模式下,日志中记录的是变更后的数据页内容。事务提交时,DELETE模式将日志刷盘,将DB文件刷盘,成功后,再将日志文件清理;WAL模式则是将日志文件刷盘,即可完成提交过程。那么WAL模式下,数据文件何时更新呢?这里引入了检查点概念,检查点的作用就是定期将日志中的新页覆盖DB文件中的老页,并通过参数wal_autocheckpoint来控制检查点时机,达到权衡读写的目的。

DELETE模式下,写事务直接更新db-page,并将old-page写入日志,读事务则直接读db-page,因为db-page中保存了提交的所有事务的更新。事务提交后,直接将日志文件删除;若事务需要回滚,则将日志中old-page中的内容覆盖db-page,恢复原始内容。WAL模式下,写事务将更新写到日志文件中,不更新db-page,事务提交时,也不影响db-page,只是将日志持久化而已。若事务回滚,则不将日志写入文件即可。由于最新的数据在日志文件中,那么如何读取到最新的数据呢?WAL模式通过end-mark(事务提交位点)达到这一目的。具体而已,事务开始时,会首先扫描日志文件,获取最近一个end-mark,在读取数据时,首先会判断page是否则在wal日志文件中存在,因为同一个page,一定是wal文件中的比db文件中的要新。如果存在,则使用,否则,再从db文件中获取指定的page。从流程上来看,这个过程比较慢,因为极端情况下,每次读都需要扫描wal文件和db文件。为了提高性能,WAL模式中有一个wal-index文件,这个文件记录了页号和该页在WAL文件中的偏移,并且wal-index文件采用共享缓存实现,从文件名也可以看到,后缀是.shm,因此判断page是否在wal文件存在的操作实质是一次内存读。wal-index采用hash表存储,因此查询效率也非常高。

与传统的DBMS不同,SQLite中记录的日志,实质是dirty-page,重做实质是对利用WAL中的日志页覆盖db-page,这种实现方式比较简单,同时也比较浪费空间,因为一个page是1k,即使只更新1byte,也会导致日志记录1k。

WAL的优势与劣势

1)  并发优势

SQLite为什么引入WAL,一定是WAL有很多好的特性。其中最主要的一点是WAL支持读写并发。在DELETE模式下,读写是互斥的。为什么WAL可以并发,而DELETE不行?我这里不打算详细展开WAL模式和DELETE模式的锁机制,后面有机会再单独写这一部分。从上面一节的分析可以知道,WAL模式下,写事务以append方式记录new-page,而读事务只会读取db-page和end-mark之前的wal日志,因此不会发生读写冲突的问题,读写可以并发。而DELETE模式下,写事务写的是db-page,读事务也是读db-page,所以读写不能并发。

2) 写性能优势

从前面的分析可知,WAL模式下,事务提交只需要写入日志文件即可,为了持久化,只需要一次fsync调用。而DELETE模式下,事务提交过程中,首先要确保日志落盘(保存old-page,用来rollback),这里需要一次fsync调用,然后再执行db文件刷盘,这里还需要一次fsync,并且修改的db-page可能是离散的,因此可能需要多余一次的fsync。此外,WAL写日志都是顺序写,相对于离散写又有很大的优势。因此DELETE模式下写性能会比WAL模式要差。测试结果也证明了这一点,这里可以参考测试报告。

3) WAL劣势

开启WAL后,每次读取page,都需要通过wal-index来确认page是否在WAL中,这个会产生一定的性能损耗。另外,会引入WAL文件,这个文件如果使用不当,可能会急剧膨胀,WAL文件变大后,意味着检索wal-index的代价也变高。而且由于SQLite一般用于端设备,空间也比较稀缺,因此要严格控制好WAL文件的大小。此外,WAL的索引文件采用共享内存实现,因此访问SQlite的进程不能跨机器。

开启WAL模式

通过命令pragma journal_mode=wal可以开启wal模式。前面我们提到开启WAL模式后,如果使用不当,可能导致WAL文件空间暴增,但我们有办法避免这种情况发生。这里主要介绍两个参数,wal_autocheckpoint和journal_size_limit。wal_autocheckpoint用来设置触发检查点的时机,默认是1000页,即当日志增长到1000页时,开始做检查点操作。这里要说明一点的是,SQLite中没有单独的检查点线程,如果设置1000,则触发写1000页的事务来进行检查点操作。因此这个事务的响应时间会比较长,而其它事务则不受影响。用来设置日志文件的大小,默认情况为-1,当这个参数设置时,若累计更新页大小超过journal_size_limit,也会导致检查点触发,用以重复利用日志文件,避免日志继续增长。

相关参数

1)  journal_mode(日志模式)

默认是DELETE模式

DELETE:原始数据页存放在日志文件中,事务提交时,将文件删除。

TRUNCATE :与DELETE模式的区别是,清空日志文件,但不删除文件清空文件往往比删除文件要快。

PERSIST:与DELETE和TRUNCATE模式区别是,既不删除文件,也不清空文件,而是将日志文件第一个页设置标记(置0),这个也是为了提高性能。 MEMORY :内存模式,修改不落盘,无法保证事务的原子性。

OFF:不开启日志,这样没法保证事务的原子性。

WAL :write ahead log,3.7.0引入,日志中记录修改页,提交时只需刷修改页。

2)  journal_size_limit(日志文件大小)

默认值为-1,表示没有限制,单位是字节。 DELETE模式下,当日志增长超过阀值时,则进行截断。 WAL模式下,当日志增长超过阀值时,日志文件不再会被截断,而是重复利用, 因为通常情况下重复写的性能要好于追加的性能,而且也省磁盘空间。 default_journal_size_limit,用于设置日志文件的默认大小。

3)  wal_checkpoint(检查点模式)

PASSIVE,默认自动检查点和主动检查点都是PASSIVE类型,将所有可以同步到db的数据都进行同步(不超过所有线程的end mark),不持有排它锁,因此不会影响其他读写事务。

FULL,将wal与db文件完全同步,需要等待所有读写事务都结束,并且会堵塞新的读写事务

RESTART,与FULL模式的区别是,下一个写线程从头开始写wal文件。 TRUNCATE,与FULL模式的区别是,将wal文件截断为0。

4) wal_autocheckpoint(检查点触发时机)

默认值为1000页,单位是页。当日志的增量到N页时,触发检查点操作,将wal_autocheckpoint设置为0或者-1,表示关闭检查点。

5) synchronous(同步模式)

默认设置是FULL

0(OFF):事务提交时,不作sync操作,直接返回。

1(NORMAL):事务提交时,不作sync操作

2(FULL):每次事务提交,都强制刷日志(WAL),强制数据页(journal)

6) cache_size 默认值2000,单位为页 修改db的缓存页数目,临时生效

7) default_cache_size

默认值2000,单位为页

修改缓存页数目,永久生效若同时设置了cache_size和default_cache_size,以default_cache_size为准

参考文档

https://www.sqlite.org/wal.html

http://www.cnblogs.com/cchust/p/4754619.html

Sqlite学习笔记(四)&&SQLite-WAL原理(转)的更多相关文章

  1. Sqlite学习笔记(四)&&SQLite-WAL原理

    Sqlite学习笔记(三)&&WAL性能测试中列出了几种典型场景下WAL的性能数据,了解到WAL确实有性能优势,这篇文章将会详细分析WAL的原理,做到知其然,更要知其所以然. WAL是 ...

  2. SQLite学习笔记(八)&&sqlite实现架构

    该系列的前面一些文章我重点讲了sqlite的核心功能,比如封锁机制,共享缓存,以及事务管理等.但对于sqlite的整体没有作一个全面的介绍,本文将从实现的层面,整体介绍sqlite的框架.各个核心模块 ...

  3. Sqlite学习笔记(五)&&SQLite封锁机制

    概述 SQLite虽然是一个轻量的嵌入式数据库,但这并不影响它支持事务.所谓支持事务,即需要在并发环境下,保持事务的ACID特性.事务的原子性,隔离性都需要通过并发控制来保证.那么Sqlite的并发控 ...

  4. SQLite学习笔记(七)&&事务处理

    说到事务一定会提到ACID,所谓事务的原子性,一致性,隔离性和持久性.对于一个数据库而言,通常通过并发控制和故障恢复手段来保证事务在正常和异常情况下的ACID特性.sqlite也不例外,虽然简单,依然 ...

  5. SQLite 学习笔记

    SQLite 学习笔记. 一.SQLite 安装    访问http://www.sqlite.org/download.html下载对应的文件.    1.在 Windows 上安装 SQLite. ...

  6. sqlite学习笔记7:C语言中使用sqlite之打开数据库

    数据库的基本内容前面都已经说得差点儿相同了.接下看看如何在C语言中使用sqlite. 一 接口 sqlite3_open(const char *filename, sqlite3 **ppDb) 打 ...

  7. Java IO学习笔记:概念与原理

    Java IO学习笔记:概念与原理   一.概念   Java中对文件的操作是以流的方式进行的.流是Java内存中的一组有序数据序列.Java将数据从源(文件.内存.键盘.网络)读入到内存 中,形成了 ...

  8. 零拷贝详解 Java NIO学习笔记四(零拷贝详解)

    转 https://blog.csdn.net/u013096088/article/details/79122671 Java NIO学习笔记四(零拷贝详解) 2018年01月21日 20:20:5 ...

  9. C#可扩展编程之MEF学习笔记(四):见证奇迹的时刻

    前面三篇讲了MEF的基础和基本到导入导出方法,下面就是见证MEF真正魅力所在的时刻.如果没有看过前面的文章,请到我的博客首页查看. 前面我们都是在一个项目中写了一个类来测试的,但实际开发中,我们往往要 ...

随机推荐

  1. DRY

    DRY(Don't Repeat Yourself )原则   凡是写过一些代码的程序猿都能够意识到应该避免重复的代码和逻辑.我们通过提取方法,提取抽象类等等措施来达到这一目的.我们总能时不时的听到类 ...

  2. Codeforces Round #256 (Div. 2) C. Painting Fence 或搜索DP

    C. Painting Fence time limit per test 1 second memory limit per test 512 megabytes input standard in ...

  3. Android 解决Gallery下ScrollView滑动事件冲突

    在Gallery下,里面内容过长超出屏幕,这时我们可以用ScrollView来滚动,但是这样做了以后,会发现一个问题,Gallery的滑动事件和ScrollView的滑动事件起冲突,这时我们可以自定义 ...

  4. Array of Objects

    You should be comfortable with the content in the modules up to and including the module "Array ...

  5. How to fix Column 'InvariantName' is constrained to be unique 解决办法!

    Introduction When you build a web project that uses Enterprise Library Community for the Application ...

  6. Dictionary带来的一种隐式内存泄漏

    当心Dictionary带来的一种隐式内存泄漏 最近在看Dictionary的源代码的时候, 突然想到Dictionary的不当使用中有一种隐含内存泄漏的可能. 简化使用场景 小A正在写一个简单的图书 ...

  7. Windows 7 USB DVD Download Tool 制作的U盘无法启动安装Windows7 SP1

    以前用此工具安装Windows7一直正常,未遇到不能启动安装的问题.Windows7 SP1出来后,用此工具制作安装多台机器均不能引导安装(品牌机和兼容机均是如此 ),要么停留在光标闪烁的状态,要么就 ...

  8. 【转】static_cast和reinterpret_cast

    static_cast和reinterpret_cast揭秘 收藏 本文讨论static_cast<> 和 reinterpret_cast<>. reinterpret_ca ...

  9. JDBC连接数据库 prepareStatement

    import java.sql.Connection; import java.sql.DriverManager; import java.sql.PreparedStatement; import ...

  10. php调用webservice的几种方法

    原文:php调用webservice的几种方法 1.WSDL模式: $soap = new SoapClient("http://192.168.6.69:8899/Service1.asm ...