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

本文涉及的产品
日志服务 SLS,月写入数据量 50GB 1个月
简介: Sqlite学习笔记(三)&&WAL性能测试中列出了几种典型场景下WAL的性能数据,了解到WAL确实有性能优势,这篇文章将会详细分析WAL的原理,做到知其然,更要知其所以然。 WAL是什么       WAL(Write ahead logging)是一种日志模式,它是一种思想,普遍应用于关系型数据库。

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

 

相关实践学习
日志服务之使用Nginx模式采集日志
本文介绍如何通过日志服务控制台创建Nginx模式的Logtail配置快速采集Nginx日志并进行多维度分析。
相关文章
|
28天前
|
存储 SQL 关系型数据库
Mysql学习笔记(二):数据库命令行代码总结
这篇文章是关于MySQL数据库命令行操作的总结,包括登录、退出、查看时间与版本、数据库和数据表的基本操作(如创建、删除、查看)、数据的增删改查等。它还涉及了如何通过SQL语句进行条件查询、模糊查询、范围查询和限制查询,以及如何进行表结构的修改。这些内容对于初学者来说非常实用,是学习MySQL数据库管理的基础。
106 6
|
28天前
|
SQL Ubuntu 关系型数据库
Mysql学习笔记(一):数据库详细介绍以及Navicat简单使用
本文为MySQL学习笔记,介绍了数据库的基本概念,包括行、列、主键等,并解释了C/S和B/S架构以及SQL语言的分类。接着,指导如何在Windows和Ubuntu系统上安装MySQL,并提供了启动、停止和重启服务的命令。文章还涵盖了Navicat的使用,包括安装、登录和新建表格等步骤。最后,介绍了MySQL中的数据类型和字段约束,如主键、外键、非空和唯一等。
64 3
Mysql学习笔记(一):数据库详细介绍以及Navicat简单使用
|
14天前
|
存储 SQL 数据库
数据库知识:了解SQLite或其他移动端数据库的使用
【10月更文挑战第22天】本文介绍了SQLite在移动应用开发中的应用,包括其优势、如何在Android中集成SQLite、基本的数据库操作(增删改查)、并发访问和事务处理等。通过示例代码,帮助开发者更好地理解和使用SQLite。此外,还提到了其他移动端数据库的选择。
21 8
|
25天前
|
Web App开发 SQL 数据库
使用 Python 解析火狐浏览器的 SQLite3 数据库
本文介绍如何使用 Python 解析火狐浏览器的 SQLite3 数据库,包括书签、历史记录和下载记录等。通过安装 Python 和 SQLite3,定位火狐数据库文件路径,编写 Python 脚本连接数据库并执行 SQL 查询,最终输出最近访问的网站历史记录。
|
1月前
|
存储 关系型数据库 数据库
轻量级数据库的利器:Python 及其内置 SQLite 简介
轻量级数据库的利器:Python 及其内置 SQLite 简介
|
1月前
|
应用服务中间件 PHP Apache
PbootCMS提示错误信息“未检测到您服务器环境的sqlite3数据库扩展...”
PbootCMS提示错误信息“未检测到您服务器环境的sqlite3数据库扩展...”
|
2月前
|
存储 API 数据库
QML使用Sqlite数据库存储ListModel数据
本文介绍了在QML中使用Sqlite数据库存储ListModel数据的方法,包括如何创建数据库、读取数据、动态添加和删除数据,以及如何在程序启动和退出时与数据库同步数据。
|
2月前
|
数据库 数据库管理
qt对sqlite数据库多线程的操作
本文总结了在Qt中进行SQLite数据库多线程操作时应注意的四个关键问题,包括数据库驱动加载、加锁、数据库的打开与关闭,以及QsqlQuery变量的使用。
119 1
|
25天前
|
存储 缓存 关系型数据库
sqlite 数据库 介绍
sqlite 数据库 介绍
35 0
|
2月前
|
SQL 关系型数据库 MySQL
php学习笔记-连接操作mysq数据库(基础)-day08
本文介绍了PHP中连接操作MySQL数据库的常用函数,包括连接服务器、设置字符集、关闭连接、选择数据库、结果集释放、获取影响行数以及遍历结果集等操作。通过书籍查询的实例演示了如何使用这些函数进行数据库操作,并提供了一个PHP操纵MySQL数据库的模板。
php学习笔记-连接操作mysq数据库(基础)-day08
下一篇
无影云桌面