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

原文地址:https://blog.csdn.net/zhaofuguang/article/details/91882487

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


SQLite架构简单,又有Json计算能力,有时会承担Json文件/RESTful的计算功能,但SQLite不能直接解析Json文件/RESTful,需要用Java代码硬写,或借助第三方类库,最后再拼成insert语句插入数据表,代码非常繁琐,这里就不展示了。参考前面的代码可知,入库的过程比较麻烦,不能只用SQL,还要借助Java或命令行。SPL是现代的数据计算语言,属于简化的面向对象的语言风格,有对象的概念,可以用点号访问属性并进行多步骤计算,但没有继承重载这些内容,不算彻底的面向对象语言。...
使用Python操作内置数据库SQLite以及MySQL数据库。
破解微信数据库密码,用python导出微信聊天记录
(Unity)SQLite 是一个软件库,实现了自给自足的、无服务器的、零配置的、事务性的 SQL 数据库引擎。SQLite 是在世界上最广泛部署的 SQL 数据库引擎。SQLite 源代码不受版权限制。本教程将告诉您如何使用 SQLite 编程,并让你迅速上手。.................................
安卓开发,利用SQLite实现登陆注册功能
相比大多数数据库而言,具有等优势,广泛应用于、等领域。
有时候,一个项目只有一个数据库,比如只有SQLite,或者MySQL数据库,那么我们只需要使用一个固定的数据库即可。但是一个项目如果写好了,有多个用户使用,但是多个用户使用不同的数据库,这个时候,我们就需要把软件设计成可以连接多个数据库的模式,用什么数据库,就配置什么数据库即可。4.Users实体类,这个实体类要和数据库一样的,形成一一对应的关系。11.Sqlite数据库,需要在代码里面创建数据库,建立表,再建立数据。8.我们开启MySQL数据库,然后进行调试,看程序的结果。2.安装SqlSugar。
基于Android的背单词软件,功能强大完整。
SQLite,是一款轻型的数据库,是遵守ACID的关系型数据库管理系统。说白了就是使用起来轻便简单,
Android的简单购物车案例
SQLite,是一款轻型的数据库,是遵守ACID的关系型数据库管理系统,它包含在一个相对小的C库中。它是D.RichardHipp建立的公有领域项目。它的设计目标是嵌入式的,而且已经在很多嵌入式产品中使用了它,它占用资源非常的低,在嵌入式设备中,可能只需要几百K的内存就够了。它能够支持Windows/Linux/Unix等等主流的操作系统,同时能够跟很多程序语言相结合,比如 Tcl、C#、PHP、Java等,还有ODBC接口,同样比起Mysql、PostgreSQL这两款开源的世界著名数据库...
Qt设计较为美观好看的登录注册界面(包含SQLite数据库以及TCP通信的应用)
SQLite是用C语言开发的跨平台小型数据库,可嵌入其他开发语言,也可在单机执行。SPL是用Java开发的跨平台的数据计算语言,可嵌入Java,可在单机执行,可以数据计算服务的形式被远程调用。两者的代码都是解释执行的。...
新建库.openDATA_BASE;新建表createtableLIST_NAME(DATA);语法:NAME关键字...<用逗号分割>删除表droptableNAME;查看表.schema查看表信息新建数据insertintoLIST_NAMEvalues();语法:CLASS,PARAMETER...,CLASS是类别,PARAMETER是参数<用逗号分割新建的
importsqlite3classDemo01:def__init__(self):self.conn=sqlite3.connect("sql_demo_001.db")self.cursor1=self.conn.cursor()self.cursor1.execute("select*fromtable_001wherename=?andid=?",('ssss&#0
 在客户端配置文件<configuration>节点下,添加:<connectionStrings>      <add name="localdb" connectionString="Data Source=config/local.db;Version=3;UseUTF16Encoding=True;" providerName="System.Data.SQLite.SQLiteFactory"/&g
提到锁就不得不说到死锁的问题,而SQLite也可能出现死锁。下面举个例子:连接1:BEGIN(UNLOCKED)连接1:SELECT...(SHARED)连接1:INSERT...(RESERVED)连接2:BEGIN(UNLOCKED)连接2:SELECT...(SHARED)连接1:COMMIT(PENDING,尝试获取EXCLUSIVE锁,但还有SHARED锁未释放,返回SQLITE_BUSY)连接2:INSERT...
SQLite是一种嵌入式数据库,它的数据库就是一个文件。由于SQLite本身是C写的,而且体积很小,所以,经常被集成到各种应用程序中,甚至在iOS和Android的App中都可以集成。Python就内置了SQLite3,所以,在Python中使用SQLite,不需要安装任何东西,直接使用。在使用SQLite前,我们先要搞清楚几个概念:表
设计思想————首先要确定有几个页面、和每个页面的大致布局由于是入门,我也是学习了不是很长的时间,所以项目比较low。。。。第一个页面,也就是打开APP的首页面:今天这个博客,先实现添加功能!:首先对主界面进行布局:其中activity_main.xml的代码为<?xmlversion="1.0"encoding="