SQLite学习笔记(七)&&事务处理
说到事务一定会提到ACID,所谓事务的原子性,一致性,隔离性和持久性。对于一个数据库而言,通常通过并发控制和故障恢复手段来保证事务在正常和异常情况下的ACID特性。sqlite也不例外,虽然简单,依然有自己的并发控制和故障恢复机制。Sqlite学习笔记(五)&&SQLite封锁机制 已经讲了一些锁机制的原理,本文将会详细介绍一个事务从开始,到执行,最后到提交所经历的过程,其中会穿插讲一些sqlite中锁管理,缓存管理和日志管理的机制,同时会介绍在异常情况下(软硬件故障,比如程序异常crash,主机掉电等),sqlite如何将数据库恢复到事务之前的状态。本文大量参考了sqlite的官方文档,结合自己的理解,希望能把这个过程说清楚。
1.事务提交
1.1 开启一个事务
在向数据库文件写数据前,sqlite首先需要访问sqlite_master表获取元数据信息,用来对SQL语句进行语义分析,判断语句的合法性。从数据库读数据第一步,是对数据库文件上一个Shared Lock。Shared Lock允许多个事务同时读一个数据库文件,但是Shared Lock会阻止写事务向数据库文件写入数据。
1.2 读数据
获取Shared Lock后,我们可以从数据库文件中读取数据了。我们假设缓存中没有我们的page,因此需要通过读文件读取我们需要的page。这里说明下,sqlite的数据库文件实质是有一个个大小相同的page组成,默认情况下,一个page大小为1024B。通常情况下,我们需要读取若干个page,并把这些page缓存在应用本地的cache中,这样下次访问就不需要再次从文件中读取。这里我们假设需要读取3个page,用绿色块表示。
1.3 获取Reserved Lock
在向数据库写数据之前,Sqlite需要获取一个Reserved Lock,Reserved Lock与Shared Lock类似,同时允许其他事务读取数据库。Reserved Lock与Shared Lock兼容,但与Reserved
Lock互斥,即同一个时刻只允许有一个Reserved
Lock。持有Reserved Lock表示事务准备要修改数据文件了,由于还没有真正修改文件,因此允许其他事务继续进行读操作,但不允许其他事务进行写数据库操作。
1.4 创建日志文件
在sqlite中,有两种日志技术,影子分页技术和WAL(write ahead log)技术。影子分页技术是sqlite默认采用的方式,后面的讨论都是基于这种假设。在操作数据文件之前,sqlite首先创建一个日志文件,并将准备要修改的page的内容写入日志,通过这种方式保留了恢复事务的所有原始信息。无论是数据库文件,还是日志文件,最基本的操作单位都是page。
1.5 修改数据
前面提到,sqlite修改数据前,先将page读到cache中,因此修改会首先修改cache中的数据。由于每个连接都有自己独立的page cache,因此写事务修改自己page cache中的数据,不会影响其他事务,其他事务依然会读到原始的page数据,不会导致脏读。下图中红色表示修改块,从图中可以看到,只有用户自身cache的page变成了红色。
1.6 刷日志文件
修改完成后,首先将日志文件写入磁盘。这个过程非常重要,只有通过刷盘操作(fsync)将日志持久化,才能在掉电的情况下,通过日志恢复数据页。同时,这个动作也非常耗时。
1.7 获取Exclusive
Lock
现在我们需要将之前对page cache的修改写入数据库文件,达到持久化目的。在这个动作之前,首先需要持有Exclusive Lock,获取该锁实际包含两个步骤,首先持有一个Pending Lock,然后再持有Exclusive Lock。Pending Lock允许持有读锁事务继续进行读操作,但不允许新的读事务进来。由于新的读事务被阻止,则将读事务数目限制在一定的范围,而已有的读事务迟早都会执行完,写事务最终可以获取到Exclusive
Lock,通过这种方式避免写事务饿死的情况。
1.8 将修改写入数据文件并刷盘
一旦持有了Exclusive
Lock,则此时sqlite中只有一个事务,没有其他读事务去读文件。因此,这时候向数据文件中写数据是安全的。为了保证写入动作真正落入磁盘,还需要进行刷盘动作。与刷日志一样,将数据文件修改刷盘动作也是为了保证掉电情况下,更新依然可以持久化,同样这个操作也很耗时。其中红色块表示修改块,此时用户进程空间,OS buffer,以及DISK都已经修改。
1.9 删除日志文件
进行这步时,日志文件和数据文件修改都已经固化到磁盘。如果在1.8步之前,发生掉电,由于日志文件已经安全落盘,因此可以将数据库恢复到事务开始前的状态。由于数据文件修改已经固化,我们可以将日志文件删除。通过日志文件的存在与否,判断我们是需要将事务回滚还是提交。由于删文件也是一个比较耗时的动作,sqlite对此进行了优化,通过参数选项,可以选择将日志全部初始化0,或是直接将文件截断,达到提高性能的目的。
1.10 释放Exclusive Lock
最后一步是释放Exclusive
Lock,这样其他事务才有机会读、写数据文件。这里有一个问题,每个连接都有自己的page cache,如果page cache中的内容已经被改了,并写入到了文件中,那么其它事务如何感知,将自己本地的old-page清理,重新从文件中读?sqlite通过一个计数器来控制,这个计数器存在数据库文件的第一个page中。每次数据文件修改时,这个值也会同时自增。事务开始时,会读取计数器,在读取page 时,会再次检查计数器是否发生变化,如果发生变化,说明有事务提交,则将本地的cache全部清空,重新从数据库文件中获取。
2.事务回滚
正常情况下,通过上述的事务提交流程,就可以保证事务的ACID特性。但是事务在执行过程中发生异常呢,这时候就需要通过事务回滚来将数据库恢复到事务开始前的状态。下面假设一种情况,来介绍回滚流程。
2.1 发生故障
假设在1.8之前,写数据库文件时,发生了掉电故障。当故障恢复后,情形可能如右图所示,只有部分页写入了磁盘,甚至有一个页可能只写入了一部分。由于执行到这个步骤时,日志已经安全落盘,因此可以借助日志进行恢复。
2.2 热日志
任何一个连接在操作数据库之前,会首先判断是否有热日志存在,因为有热日志存在意味着可能需要故障恢复。所谓热日志,是指需要事务提交过程中发生了故障,需要利用日志恢复。
2.3 回滚未完成的操作
在利用日志进行恢复前,首先持有Exclusive
Lock,这样避免多个连接同时进行故障恢复,持有Exclusive
Lock后,才可以开始修改数据库文件。sqlite从日志文件中读取原始的数据页,然后将数据页写回到数据文件中。由于日志文件头部记录了事务开始时数据文件的大小,sqlite利用这个信息来讲数据文件进行截断到原来的大小,保持文件大小恢复到事务开始时的水平。
2.4 删除日志文件
当所有日志文件中的数据页都已经拷贝到数据文件中后,进行一次刷盘操作,确保修改持久化,这时候日志文件可以被删除了。恢复完成后,将Exclusive Lock 降级到Shared Lock。这个过程完成后,数据库完成恢复。由于整个过程都是sqlite自动完成,用户完全无感知。对于用户而言,任何时候使用sqlite操作数据文件都是安全的,即使在发生了异常的情况下。
参考文档
https://www.sqlite.org/atomiccommit.html
SQLite学习笔记(七)&&事务处理的更多相关文章
- SQLite 学习笔记
SQLite 学习笔记. 一.SQLite 安装 访问http://www.sqlite.org/download.html下载对应的文件. 1.在 Windows 上安装 SQLite. ...
- Sqlite学习笔记(四)&&SQLite-WAL原理
Sqlite学习笔记(三)&&WAL性能测试中列出了几种典型场景下WAL的性能数据,了解到WAL确实有性能优势,这篇文章将会详细分析WAL的原理,做到知其然,更要知其所以然. WAL是 ...
- (转)Qt Model/View 学习笔记 (七)——Delegate类
Qt Model/View 学习笔记 (七) Delegate 类 概念 与MVC模式不同,model/view结构没有用于与用户交互的完全独立的组件.一般来讲, view负责把数据展示 给用户,也 ...
- Sqlite学习笔记(四)&&SQLite-WAL原理(转)
Sqlite学习笔记(三)&&WAL性能测试中列出了几种典型场景下WAL的性能数据,了解到WAL确实有性能优势,这篇文章将会详细分析WAL的原理,做到知其然,更要知其所以然. WAL是 ...
- Learning ROS for Robotics Programming Second Edition学习笔记(七) indigo PCL xtion pro live
中文译著已经出版,详情请参考:http://blog.csdn.net/ZhangRelay/article/category/6506865 Learning ROS forRobotics Pro ...
- Typescript 学习笔记七:泛型
中文网:https://www.tslang.cn/ 官网:http://www.typescriptlang.org/ 目录: Typescript 学习笔记一:介绍.安装.编译 Typescrip ...
- python3.4学习笔记(七) 学习网站博客推荐
python3.4学习笔记(七) 学习网站博客推荐 深入 Python 3http://sebug.net/paper/books/dive-into-python3/<深入 Python 3& ...
- Go语言学习笔记七: 函数
Go语言学习笔记七: 函数 Go语言有函数还有方法,神奇不.这有点像python了. 函数定义 func function_name( [parameter list] ) [return_types ...
- iOS 学习笔记七 【博爱手把手教你使用2016年gitHub Mac客户端】
iOS 学习笔记七 [博爱手把手教你使用gitHub客户端] 第一步:首先下载git客户端 链接:https://desktop.github.com 第二步:fork 大神的代码[这里以我的代码为例 ...
随机推荐
- swift 如何实现点击view后显示灰色背景
有这样一种场景,当我们点击view的时候,需要过0.几秒显示一个灰色或者别的颜色的背景 用button来实现,只有按下去的时候才会出现,往往在快速按下,快速抬起的时候是看不出这个变化的 下边是解决方案 ...
- Android百度地图 关于visibility="gone"的奇葩问题
最近在项目中遇到一个奇葩问题,花了很长时间,在这里记录下. 问题描述:我的主界面是ViewPager+Fragment,并且设置缓存了我的4个ViewPager页面.左侧是一个侧滑菜单,点击相应按钮打 ...
- ZOJ Problem Set - 1115 Digital Roots
水题记录: 注:此题题目并没有限定数值的大小,所以要用字符串进行处理 #include <stdio.h> #include <string.h> int main() { ] ...
- 遍历迭代map的集中方法
public static void main(String[] args) { Map<String, String> map = new HashMap<String, Stri ...
- 关于JavaScript定时机制的总结
要理解JavaScript的定时机制,就要知道JavaScript的运行机制. 首先声明,JavaScript是单线程运行(JavaScript引擎线程)事件驱动. 一.浏览器中有多个线程 一款浏览器 ...
- 【JS】heatmap.js v1.0 到 v2.0,详细总结一下:)
前段时间,项目要开发热力图插件,研究了heatmap.js,打算好好总结一下. 本文主要有以下几部分内容: 部分源码理解 如何迁移到v2.0 v2.0官方文档译文 关于heatmap.js介绍,请看这 ...
- Android学习笔记之ListView复用机制
PS:满打满算,差不多三个月没写博客了...前一阵忙的不可开交...总算是可以抽出时间研究研究其他事情了... 学习内容: 1.ListView的复用机制 2.ViewHolder的概念 1.List ...
- Go项目的目录结构
项目目录结构如何组织,一般语言都是没有规定.但Go语言这方面做了规定,这样可以保持一致性,做到统一.规则化比较明确. 1.一般的,一个Go项目在GOPATH下,会有如下三个目录: |--bin |-- ...
- 我理解的this
this指的就是当前上下文环境对象,主要分两种情况. 1.函数中的this指的是调用该函数的那个上下文环境对象 这个的理解还是非常重要的. 看一个全局函数的例子 var b = 1; function ...
- Win10 UWP系列:更新UWP时注意的问题——TargetDeviceFamily
前几天把CurrencyExchanger提交到微软参加Master认证,结果没有通过,反馈了一些错误,看来微软检查还是比较仔细的. 错误主要有: Visual feedback helps user ...