Sqlite的事务,锁和WAL模式

栏目: IOS · 发布时间: 5年前

内容简介:事务定义了一组SQL语句的边界,这组SQL语句要么都执行,要么都不执行,事务保证了数据库完整性中的原子性。事务与数据库锁是紧密相关的,Sqlite用锁来保证事务的并发和执行顺序。在讲解事务的原理之前,我们先看看事务相关的API,主要有三个:我们首先来看看回滚,假设有这样的一个Table

前言

事务定义了一组 SQL 语句的边界,这组SQL语句要么都执行,要么都不执行,事务保证了数据库完整性中的原子性。事务与数据库锁是紧密相关的,Sqlite用锁来保证事务的并发和执行顺序。

在讲解事务的原理之前,我们先看看事务相关的API,主要有三个:

  • begin transaction 开始一个事务。其中,transaction可省略,直接写 begin 即可。

  • rollback 回滚。

  • commit 提交

我们首先来看看回滚,假设有这样的一个Table

id          name        phone     
----------  ----------  ----------
10001       Leo         1234567890

接着,我们来试试回滚

//开始事务
sqlite> begin;
//插入一条数据
sqlite> insert into person(id,name,phone) values(10002,"Lina","12345671");
//查询,数据确实多了一行
sqlite> select * from person;
id          name        phone     
----------  ----------  ----------
10001       Leo         1234567890
10002       Lina        12345671  
//回滚
sqlite> rollback;
//插入的数据被删除
sqlite> select * from person;
id          name        phone     
----------  ----------  ----------
10001       Leo         1234567890

提交也类似,这样的语法结构即可:

begin;
//SQL语句
commit;

事务的存在保证了数据库的的原子性,比如在数据写入的时候出现异常(断电等原因),待写入的数据不会丢失。

多链接模型

常见的模型:一个数据库文件,多个连接,每个连接有自己的语句。

Sqlite的事务,锁和WAL模式

一些名词的解释:

Connection - 连接,表示到数据库连接和事务的上下文,对应C接口中的 sqlite 3类型指针。一个数据库文件可以同时存在多个connection。

statement - 语句。 语句对象表示一个编译过的SQL语句,在内部由VDBE字节码来表示。VDBE全称是(Virtual DataBase Engine),也就是Sqlite虚拟机。一个这样的SQL语句select * from Table,在SQLite中是先要编译成VDBE字节码才能执行的。

每个连接都对应着一个B-Tree和Pager。连接直接操作B-Tree来读和写数据。当一个连接需要读取数据库文件的某一页的时候,这一页必须先加载到内存里,这时候B-Tree操作Pager进行页面读取,同时进行缓存。由于数据库要保证ACID,所以Pager还负责缓存管理以及回滚日志的维护。

OS Interface。 操作系统层API,比如Windows/MacOS对于文件的操作API是不一样的,这一层进行了封装,保证上层调用的统一。

Journal,日志文件,用于回滚。

事务是由Pager模块管理的,当一个事务开始( begin )后,遇到了更改数据库的语句( update , insert , delete ),会首先操作当前连接的Pager,然后创建回滚日志文件,等到事务提交( commit ),Pager才会把整个事务的修改同步到数据库文件里。

在写文件的时候,无法避免的要锁数据库文件,锁数据库文件后,其他连接就无法读/写数据库。事务这种延迟和批量写入的方式,能够最大限度的提高数据库的并发性能。

当两个连接同时操作数据库进行读写的时候,SQLITE如何保证事务的正确性,以及如何保证读和读是可以并发的呢?答案是不同优先级的锁。

大多数时候,锁的持续时间和事务是一样的,虽然并不是同时开始,但是总是一起结束。

Sqlite采用粗粒度的锁。当一个连接尝试写数据库的时候,所有其他连接都被锁住,直到写连接结束它的事务。Sqlite有一个加锁表,用来帮助不同的写数据库都能在最后一刻加锁。

Sqlite的事务,锁和WAL模式

状态:

  • UNLOCKED 未加锁

  • SHARED 共享锁

  • RESERVED 预留锁

  • PENDING 未决锁

  • EXCLUSIVE 排它锁

每个数据库连接同时只能处于一个状态 ,多个连接可以同时处于共享锁状态,哪怕有一个共享锁没有被释放,都不允许往数据库中写入内容。

在尝试读数据库的时候,从连接UNLOCKED转为SHARED(共享锁),由于共享所可以同时存在,其他连接也可以获取SHARED(共享锁),从而实现读和读的并发。

锁的变化状态:

UNLOCKED -> PENDING -> SHARED -> UNLOCKED

一个连接写数据库的时候:

首先由UNLOCKED转换为SHARED(共享锁)

共享锁升级为预留锁,统一时间只能有一个连接处于预留锁状态,这时候Pager初始化回滚日志,用于故障恢复,当B-Tree修改页的时候,这些页都会存储到日志文件里。

事务提交,锁升级为PENDING,此时阻止其他连接获取共享锁,等待已经获取共享锁的连接执行完毕。

升级为EXCLUSIVE,开始实际操作数据库文件

锁状态变化:

UNLOCKED -> SHARED -> RESVERED -> PENDING-> EXCLUSIVE -> UNLOCKED

Sqlite有一个配置是synchronous,这个配置保证了数据从内存中写入到了磁盘,如果配置为OFF,效率提高50倍,但是无法保证持久性。

等待锁

一个SELECT语句在执行的时候,必须获得数据库的共享锁,若此时正好有一个活跃的写操作,那么SELECT语句无法获取共享锁,会返回 SQLITE_BUSY 。可以设置超时时间:

sqlite3_busy_timeout()

死锁

两个连接A和B:

B开始事务,尝试写入,这时候获取保留锁,开始操作Pager。此时A获取共享锁,A尝试写入,尝试转换到保留锁失败,这时候A处于共享状态,等待升级为可写入状态。B commit的时候,因为共享锁的存在,所以无法进入独占锁,导致死锁。

一个语句在遍历表的时候,同时进行写入表,也可能会造成死锁。

事务模式

可以以三种方式开始事务:

begin deferred(延迟) 默认就是这种模式,以这种模式开始的事务一开始不获取任何锁,是处于unlocked状态的。对数据库第一次读的时候,获取共享锁,对数据库第一次写的时候,获取保留锁。

begin immediate。事务开始的时候尝试获取保留锁,获取成功后别的连接就不能写了,但是可以读,commit的时候可能会返回SQLITE_BUSY,这意味着需要等待其他读操作完成。

begin exclusive。事务开始的时候尝试获取独占锁,这样其他连接无法进行读写。

可以用事务模式来解决死锁的问题,例子中的死锁问题,A和B可以用begin immediate来开始事务,这样就不会存在B等待A释放共享锁。

事务冲突

Sqlite提供几种策略来处理

replace 由新的一行来替换有冲突的行

ignore 跳过有冲突的行

fail 到有冲突的行终止命令,但是不恢复之前已经修改的记录。

abort(默认) 终止命令,恢复命令做的修改

rollback(最严格),终止命令和事务,回滚整个事务

WAL

WAL的全称是write ahead log,使用WAL文件能够提高数据库并发性能。

WAL文件替代了回滚日志,它的优点:

大多数情况下,速度有质的提升

更好的并发性能,由于读不回block写,写也不会block读

磁盘I/O操作更序列

使用更少的fsync(),所以在低质量磁盘上更不容易出问题

缺点:

无法改变Page的大小

WAL在读数量远高于写数量的应用中会略慢1%-2%

生成额外的WAL文件

WAL的原理:

对数据库修改是是写入到WAL文件里的,这些写是可以并发的(WAL文件锁)。所以并不会阻塞其语句读原始的数据库文件。当WAL文件到达一定的量级时(CheckPoint),自动把WAL文件的内容写入到数据库文件中。

当一个连接尝试读数据库的时候,首先记录下来当前WAL文件的末尾 end mark)。然后,先尝试WAL文件里查找对应的Page,通过WAL-Index来对查找加速(放在共享内存里),如果找不到再查找数据库文件。

WAL要定期同步到数据库文件里,大的WAL文件会降低读读性能。WAL越小,读性能越好,但是写性能越差,默认是1000个Page。

可以通过sqlite3_wal_checkpoinnt()来强制进行checkpoint。

WAL模式的数据库要求可写权限,因为wal-index文件需要在第一次访问的时候重新生成。

--------------------- 

作者:黄文臣 

原文:https://blog.csdn.net/Hello_Hwc/article/details/79647470 


以上所述就是小编给大家介绍的《Sqlite的事务,锁和WAL模式》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

疯狂Java讲义

疯狂Java讲义

李刚 / 电子工业出版社 / 2008-10 / 99.00元

《疯狂Java讲义》2000年至今,Java语言一直是应用最广的开发语言,并拥有最广泛的开发人群。如今,Java已经不再简单地是一门语言,它更像一个完整的体系,一个系统的开发平台。更甚至,它被延伸成一种开源精神。 《疯狂Java讲义》深入介绍了Java编程的相关方面,全书内容覆盖了Java的基本语法结构、Java的面向对象特征、Java集合框架体系、Java泛型、异常处理、Java GUI编......一起来看看 《疯狂Java讲义》 这本书的介绍吧!

Base64 编码/解码
Base64 编码/解码

Base64 编码/解码

Markdown 在线编辑器
Markdown 在线编辑器

Markdown 在线编辑器