Sqlite学习笔记(四)&&SQLite-WAL原理
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模式采用影子分页技术(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表存储,因此查询效率也非常高。
与传统的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可能是离散的,也会影响性能。而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为准
参考文档
https://www.sqlite.org/wal.html
Sqlite学习笔记(四)&&SQLite-WAL原理的更多相关文章
- Sqlite学习笔记(四)&&SQLite-WAL原理(转)
Sqlite学习笔记(三)&&WAL性能测试中列出了几种典型场景下WAL的性能数据,了解到WAL确实有性能优势,这篇文章将会详细分析WAL的原理,做到知其然,更要知其所以然. WAL是 ...
- SQLite学习笔记(八)&&sqlite实现架构
该系列的前面一些文章我重点讲了sqlite的核心功能,比如封锁机制,共享缓存,以及事务管理等.但对于sqlite的整体没有作一个全面的介绍,本文将从实现的层面,整体介绍sqlite的框架.各个核心模块 ...
- Sqlite学习笔记(五)&&SQLite封锁机制
概述 SQLite虽然是一个轻量的嵌入式数据库,但这并不影响它支持事务.所谓支持事务,即需要在并发环境下,保持事务的ACID特性.事务的原子性,隔离性都需要通过并发控制来保证.那么Sqlite的并发控 ...
- SQLite学习笔记(七)&&事务处理
说到事务一定会提到ACID,所谓事务的原子性,一致性,隔离性和持久性.对于一个数据库而言,通常通过并发控制和故障恢复手段来保证事务在正常和异常情况下的ACID特性.sqlite也不例外,虽然简单,依然 ...
- SQLite 学习笔记
SQLite 学习笔记. 一.SQLite 安装 访问http://www.sqlite.org/download.html下载对应的文件. 1.在 Windows 上安装 SQLite. ...
- sqlite学习笔记7:C语言中使用sqlite之打开数据库
数据库的基本内容前面都已经说得差点儿相同了.接下看看如何在C语言中使用sqlite. 一 接口 sqlite3_open(const char *filename, sqlite3 **ppDb) 打 ...
- Java IO学习笔记:概念与原理
Java IO学习笔记:概念与原理 一.概念 Java中对文件的操作是以流的方式进行的.流是Java内存中的一组有序数据序列.Java将数据从源(文件.内存.键盘.网络)读入到内存 中,形成了 ...
- 零拷贝详解 Java NIO学习笔记四(零拷贝详解)
转 https://blog.csdn.net/u013096088/article/details/79122671 Java NIO学习笔记四(零拷贝详解) 2018年01月21日 20:20:5 ...
- C#可扩展编程之MEF学习笔记(四):见证奇迹的时刻
前面三篇讲了MEF的基础和基本到导入导出方法,下面就是见证MEF真正魅力所在的时刻.如果没有看过前面的文章,请到我的博客首页查看. 前面我们都是在一个项目中写了一个类来测试的,但实际开发中,我们往往要 ...
随机推荐
- 基于吉日嘎拉的通用权限管理Webform版老界面bug修复
虽然弄了新界面<基于吉日嘎底层架构的通用权限管理Web端UI更新:参考DTcms后台界面>,但老界面的一点菜单显示的问题还是让我这种强迫症揪心,终于今晚可以美美的睡觉了. 老代码用了Ses ...
- Struts 2.0全面分析
Struts 2.0 struts 1---前端控制+应用控制+命令模式 webwork ---控制器+拦截器+代理 Struts 2.0 继承了WebWork的设计理念,并没有继承struts 1, ...
- 初学C++之自定义类型名简化
说明:本人使用的是vc++ IDE:vs2013 我在自定义一些类时,有时会取一些很长的名字,但是这不利于使用,这个时候就可以使用类型名简化. class MathAddBBBB { }; using ...
- CAS客户端服务器端配置步骤
来自我的个人网站:http://lkf.22web.org/ cas介绍: CAS 是 Yale 大学发起的一个开源项目,旨在为 Web 应用系统提供一种可靠的单点登录方法,CAS 在 2004 年 ...
- [iOS] 建立与使用Framework
[iOS] 建立与使用Framework 前言 使用XCode开发iOS项目时,开发人员可以将可重用的程序代码,封装为Library或是Framework来提供其他开发人员使用.这两种封装方式在使用的 ...
- HTML+AngularJS+Groovy如何实现登录功能
AngularJS是一款优秀的前端JS框架,已经被用于Google的多款产品当中.AngularJS核心特性有:MVVM.模块化.自动化双向数据绑定.语义化标签.依赖注入等.AngularJS认为声明 ...
- angularjs——插值字符串
一.何为插值字符串? 其实插值字符串的意思就是:拥有字符插值标记的字符串.如: hello,{{ to }}....字符插值标记:相当于我们平时在字符串替换中使用到的占位符.上面的例子中的{{to}} ...
- 在64位Windows7上安装64位Oracle11g
我一直在用Oracle10g数据库,最近想看看11g怎么样,就试着装了一下,在安装过程中遇到的麻烦还不少,幸好有搜索引擎,根据前辈的指点,磕磕绊绊地也将Oracle装上了,作一下记录,以后也许能用得着 ...
- JavaScript——99乘法表
<!DOCTYPE html> <html> <head> <title>99乘法表</title> <style type=&quo ...
- C语言 指针例解
在计算机科学中,指针(Pointer)是编程语言中的一个对象,利用地址,它的值直接指向(points to)存在电脑存储器中另一个地方的值.由于通过地址能找到所需的变量单元,可以说,地址指向该变量单元 ...