Sqlite学习笔记(三)&&WAL性能测试中列出了几种典型场景下WAL的性能数据,了解到WAL确实有性能优点,这篇文章将会详细分析WAL的原理,作到知其然,更要知其因此然。html
WAL是什么sql
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模式采用影子分页技术(Shadow paging),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表存储,所以查询效率也很是高。app
与传统的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,因此读写不能并发。spa
2) 写性能优点
从前面的分析可知,WAL模式下,事务提交只须要写入日志文件便可,为了持久化,只须要一次fsync调用。而DELETE模式下,事务提交过程当中,首先要确保日志落盘(保存old-page,用来rollback),这里须要一次fsync调用,而后再执行db文件刷盘,这里还须要一次fsync,而且修改的db-page多是离散的,也会影响性能。而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. WAL模式下,检查点是否会致使锁等待?
检查点包括自动检查点和手动检查点。经过PRAGMA wal_autocheckpoint=N命令,能够设置自动检查点,当N<=0时,自动检查点关闭,全部自动检查点的类型都是PASSIVE。默认状况下,自动检查点是开启的,N为1000。对于PASSIVE类型的检查点,不会影响读写事务。经过 PRAGMA schema.wal_checkpoint 命令能够手工触发一次检查点,好比PRAGMA schema.wal_checkpoint(PASSIVE)触发一次PASSIVE类型检查点,PRAGMA schema.wal_checkpoint(FULL)触发一次FULL类型检查点。对于FULL类型,因为须要将全部更新合并到DB文件,若是有读写事务没有结束,则须要等待;并且作检查点过程当中,会堵塞新的读写事务。因此PASSIVE类型不会致使锁等待,而FULL类型,RESTART和TRUNCATE类型,会致使锁等待。
2. 检查点是否会致使事务响应时间变长?
对于自动检查点,根据wal_autocheckpoint=N设置,当更新page超过N时会触发一次检查点,那么当前的这个事务就须要等检查点执行完毕才返回,因此触发检查点的事务响应时间会变长。
3.WAL隔离级别是什么?
WAL模式下,读写能够并发,事务可否得到新数据的关键点在于wal-index的位点,SQLite中,只会在每一个事务开始时获取一次位点,事务中屡次读位点都是同一个,所以隔离级别是可重复读。
相关参数
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为准
参考文档