事务

概述

事务定义了一组SQL命令的边界, 这组命令作为一个整体被全部执行,或者都不执行,这被称为数据库完整性的原子性原则.事务和锁在数据库操作中密切相关,数据操作总是在事务中执行,事务又涉及到锁,如果控制不当,会导致很多问题.所以我们应该看懂自己写的代码并能指出事务的状态,或者至少能够发现潜在问题.

事务的范围

事务由3个命令控制:begin, commit和rollback, begin开始一个事务, begin之后的所有操作都可以被取消. commit提交事务开始后的所有执行的操作, rollback 还原begin之后的所有操作. 默认情况下, SQLite中每条语句自成事务. 也就是SQLite默认每条单独的SQLu\语句就是begin ... commit/rollback的事务.这种情况下, 所有成功完成的命令自动提交.同样遇到错误命令都会回滚.

事务生命周期

代码和事务需要考虑一些问题:

  • 事务在哪些对象上运行
  • 事务的持续事件
  • 事务与锁的关联

事务在哪些对象上运行

一个连接对象代表到数据库的连接和事务上下文,statement来自连接对象.一个statement代表了一个编译的SQL语句.每个连接都有一个B-tree和与之关联的pager,连接处理相关操作,实际是pager代劳处理.数据库写操作时,是在用一个连接,一次一个事务.因此所有的对象都是运行在派生它们自身的连接的单个事务上下中.

事务的持续时间

事务持续的时间与单个语句一样短暂, 或者可以很长.默认情况下,连接运行在自动提交模式下,这意味着发出的每个命令都运行一个单独的事务.事务当持续到调用COMMIT或者rollback,或者SQL命令引起约束违反进而导致rollback.

事务与锁的关联

大多数情况下,锁持续时间隐藏在事务的持续时间内,二者不总是一起开始,但总是一起结束,释放于其相关的锁.SQLite有5中不同的锁状态,连接总是处于其中之一. 白色的锁状态-未锁定,待定,共享和保留,全都可以在同一时间同一数据库的不同连接中存在.不过从灰色的待定锁开始,限制就多了,灰色的待定状态代表锁正在被某个连接锁拥有,即某个想要获取独占锁的写操作.

读事务
begin;
select * from episodes;
select * from episodes;
commit;

这里锁的状态 UNLOKED -> PENDING -> SHARED -> UNLOCKED.

如果去除begin,commit,两条select命令运行在自动提交模式下, 因此他们经历路径是UNLOKED -> PENDING -> SHARED -> UNLOCKED-> PENDING -> SHARED -> UNLOCKED.

写事务

单个命令的锁状态路径 UNLOKED -> PENDING -> SHARED ->RESERVED -> PENDING -> EXCULSIVE -> UNLOCKED

  • RESERVED 状态 连接尝试着向数据库写入内容时,必须从共享锁转换到预留锁.如果获得了预留锁,则准备好开始对数据修改.连接在此时修改数据库,它也可以修改内容存储在本地pager内的内存缓存中.因为页面缓存,写操作连接可以在预留锁完成实际工作,而不干扰其他读操作,有效的地让多个读操作和写操作同一时间在同一数据库中工作.

  • PENDING 状态 当连接完成了写操作,并提交事务时,pager开始进入独占状态的过程.一旦获取了待定锁,并继续持有该锁,其他连接无法从未待定转换到共享状态,结果是没有可以进入数据库的新连接.当其他连接释放了该锁,数据库就进入写操作, pager从待定状态转换到独占状态.

  • EXCLUSIVE 独占状态主要工作是将修改的页从页面缓存刷新到数据库文件.pager将所有已修改的页复制到数据库文件.事务提交,从排他锁回到未锁定状态.如果该事务未提交,pager继续持有独占锁,直到发出COMMIT或者ROLLBACK状态.

锁的实现

SQLite的锁是基于标准的文件锁定来实现的.SQLite在数据库文件中有三种不同的文件锁: 保留锁, 待定锁和一个共享区域. 一切从待定字节开始.若要从未锁定移动到共享,连接首先尝试在待定字节上获取读锁.如果成功,在共享区域的任意字节上获取读锁,并释放待定字节商的读锁.若要从共享状态移动到保留状态,连接尝试获取对保留字节上的写入锁,若要从保留状态转换到独享状态,连接尝试获取待定字节上的写入锁.因为其他连接不能从待定锁中获取读锁,从而进入独享状态.最后,想要获取排它锁,连接尝试从共享区域获取写入锁.由于共享区域有其他连接的读取锁,此步骤保证了只有其他共享锁释放后,才能属于排它锁.

updatedupdated2020-06-132020-06-13