Sqlite学习笔记(五)SQLite封锁机制
概述
SQLite虽然是一个轻量的嵌入式数据库,但这并不影响它支持事务。所谓支持事务,即需要在并发环境下,保持事务的ACID特性。事务的原子性,隔离性都需要通过并发控制来保证。那么Sqlite的并发控制是怎样的,如何实现,在这里跟大家分享下我的理解。
SQLite是一个文件数据库,所有的数据都在一个db文件中,对于wal模式,还包含wal索引文件和wal日志文件。SQlite支持库级并发,即允许多个读事务同时运行,同一时刻最多只有一个写事务,读写冲突,相对于传统的DBMS支持表级,行级甚至MVCC,SQLite的库级并发确实显得比较寒碜。但是锁粒度越细,意味着维护锁的成本越高,系统也会越复杂,因此SQLite的封锁机制要简单很多,对资源的消耗也非常少。SQLite 3.7版本后,对并发控制做了优化,推出了WAL日志模式,可以实现读写并发,但同一个时刻仍然只能有一个写事务。由于SQLite的实现方式,SQLite只支持两种隔离级别,串行化和读未提交。读未提交,就是读全程不上锁;串行化在事务开启时上读锁,上锁和释放锁同样遵守两阶段锁协议,在事务提交或回滚时才释放锁。
文件锁
要说清楚SQLite锁实现机制,首先要了解文件锁,因为SQLite所有锁实现都是基于文件锁。对于Linux系统,文件锁主要包含两类,协同锁和强制锁,协同锁类似于互斥量,需要参与者都遵守游戏规则,在操作文件前,都先上锁,而强制锁由OS内核强制实行。协同锁根据锁粒度分为文件级别和范围级别。锁文件是最简单的对文件加锁的方法,每个需要加锁的数据文件都有一个锁文件(lock file)。当锁文件存在时,就认为该数据文件已经被加锁,别的进程不应该访问。当锁不存在,进程就可以创建一个锁文件,然后访问相应的数据文件。只要创建锁的过程是原子的,就能保证某一时刻只有一个进程拥有该锁,这种方法保证某一时刻只有一个进程访问文件。文件锁的弊端显而易见,并发粒度太低。范围锁相对于文件锁,可以锁文件的一部分内容,并且有读锁和写锁。对于同一部分内容,读锁可以共存,读锁和写锁互斥。POSIX标准提供接口fcntl()来实现。
锁类型
SQLite中的锁正是利用了范围锁来实现并发控制的目的。SQLite中主要包含了4种锁:共享锁(SHARED_LOCK)、保留锁(RESERVED_LOCK)、未决锁(PENDING_LOCK)和排它锁(EXCLUSIVE_LOCK),这4种锁定义了3个区域,其中共享锁和排它锁占用文件相同的区域。具体而言,SQLite定义了文件的以下区域为锁文件区域,由于fcntl可以对不存在的文件区域加锁,因此 PENDING_BYTE定位在区域1G的地方,即使DB文件没这么大也不影响。三种类型的锁,分别在1G,1G+1,1G+2的偏移处,之所以SHARED_SIZE长度是510,原因在于windows环境下,LockFile()加锁区域不能重叠(Linux没有这种问题),对于同一个字节上锁会影响并发,因此设置了一个范围,对SHARED_FIRST—SHARED_FIRST+ SHARED_SIZE范围内的随机数进行加锁,这样可以减少冲突,保证高效的读取文件。具体锁类别和说明参见表1
锁类别 |
字节范围 |
说明 |
PENDING_BYTE |
0x40000000 |
一种过渡锁,读事务获取读锁,写事务获取写锁前,都需要获取该锁。 |
RESERVED_BYTE |
0x40000001 |
表示线程要开始写操作,某一时刻只能有一个RESERVED Lock,但是RESERVED锁和SHARED锁可以共存,而且可以对数据库加新的SHARED锁。 |
SHARED_LOCK |
0x40000002-0x40000200 |
共享锁,开启事务时,都需要获取该锁 |
EXCLUSIVE_LOCK |
0x40000002-0x40000200 |
排它锁 |
表1
从各个锁的作用来看,不免会疑问,为啥要加上RESERVED_LOCK和PENDING_LOCK两种类型,直接通过共享锁和排它锁不就可以达到读读共享,读写互斥的目的了吗。这里引入这Reserved锁的目的是为了提高并发。由于SQLite只有库级排斥锁(EXCLUSIVE LOCK),如果写事务一开始就上EXCLUSIVE锁,然后再进行实际的数据更新,写磁盘操作,这会使得并发性大大降低。而SQLite一旦得到数据库的RESERVED锁,就可以对缓存中的数据进行修改,而与此同时,其它进程可以继续进行读操作。直到真正需要写磁盘时才对数据库加EXCLUSIVE锁。Pending锁的作用主要是为了防止写饿死的情况,写事务获取Pending锁后,新的读事务无法再进来,然后再加EXCLUSIVE锁,这样写事务获取锁的几率大大提高,读写事务的流程如下表2,状态变迁图如图1。
类型 |
操作 |
锁信息 |
说明 |
读事务 |
begin |
不持有锁 |
|
select c1 from user where id=1 |
Lock: Pending(Read) Lock:Shared(Read) Unlock:Pending |
获取Shared读锁前,需要先获取Pending共享锁, 通过这种方式与写事务互斥。 |
|
commit |
UnLock:Shared |
||
写事务 |
begin |
||
Update c1=c1+1 where id=1 |
Lock: Pending(Read) Lock:Shared Unlock:Pending Lock:Reserved(Write) |
先获取Shared读锁,然后获取Reserved的排它锁,阻止其它写事务 |
|
commit |
Lock:Pending(Write) Lock:Exclusive(Write) Unlock: Pending Unlock: Exclusive(Write) |
获取Pending的排它锁,阻止新的读事务,最后上排它锁,阻止所有读事务,读写不能并发 Pending锁方式好处是,减少写饿死的几率。 |
表2
图1
Wal锁类型
引入WAL机制后,SQLite开始支持读写并发,并且引入了WAL日志文件锁。WAL日志锁实质是锁wal-index文件的区域,根据不同的锁类型,将wal-index文件的不同区域划定义成不同的锁,主要有读锁,写锁,检查点锁,具体如表3,4。WAL模式下,最新的数据位于日志文件中,无论是读事务还是写事务都需要持有WAL_READ_LOCK的读锁,因为它们都需要获取最新的事务点。因此,做检查点时,可以通过对WAL_READ_LOCK位置(124-127)上锁,来确定检查点需要等待还是停止推进。同时我们也可以看到,对于DB文件,读写事务都只需要对DB文件上读锁,对于WAL日志文件,WAL_READ_LOCK和WAL_WRITE_LOCK位于不同的位置,读写相互不影响,所以读写不互斥。
锁类别 |
字节范围 |
说明 |
|||
读事务(WAL) |
begin |
||||
select c1 from user where id=1 |
DB文件: Lock: Pending(Read) Lock:Shared Unlock:Pending WAL文件: Lock:WAL_READ_LOCK(Read) |
除了获取DB文件锁,还需要获取WAL锁,得到最新提交事务的位点。 若有事务再作检查点,需要重试多次。 |
|||
commit |
Unlock:WAL_READ_LOCK Unlock:Shared |
||||
写事务(WAL) |
begin |
||||
Update c1=c1+1 where id=1 |
DB文件: Lock: Pending-Read Lock:Shared(Read) Unlock:Pending WAL文件: Lock:WAL_READ_LOCK(Read) Lock:WAL_WRITE_LOCK(Write) |
通过 EXCLUSIVE-WRITE-LOCK控制写写并发 由于不操作DB文件,因此不存在读写冲突,读写可以并发。 |
|||
commit |
WAL文件: Lock:SHARED-READ-LOCK Unlock:WAL_READ_LOCK(Read) Unlock: WAL_WRITE_LOCK(Write) DB文件: Unlock:Shared |
获取SHARED-READ-LOCK目的是为了获取最新提交日志的位点 |
|||
检查点 操作 (WAL) |
WAL文件: Lock:WAL_CKPT_LOCK(write) Lock:WAL_READ_LOCK(write) UnLock:WAL_READ_LOCK UnLock:WAL_CKPT_LOCK |
EXCLUSIVE-CKPT-LOCK WAL_READ_LOCK阻止读写事务。 |
表3
锁类别 |
字节范围 |
说明 |
WAL_WRITE_LOCK |
120 |
写锁位置 |
WAL_CKPT_LOCK |
121 |
检查点锁位置 |
WAL_RECOVER_LOCK |
122 |
故障恢复锁位置 |
WAL_READ_LOCK |
123 |
读锁(表示不需要wal文件) |
124-127 |
读锁(每个位置,对应一个锁) 做检查点时,逐一对每个位置上写锁,若上锁失败表示对应位置上的读事务没有结束,根据检查点策略确定是等待(FULL),还是停止推进(PASSIVE)。 |
表4
调试
SQLite通过几个宏定义可以打印语句执行的锁信息,方便大家了解语句执行中加了哪些锁,什么时候加的,什么时候释放的,以及如何处理锁冲突。具体的宏包括SQLITE_LOCK_TRACE,SQLITE_FORCE_OS_TRACE,和SQLITE_DEBUG,具体可以在代码中查看宏定义的注释。
gcc sqlite3.c -g -lpthread -ldl -fPIC -shared -DSQLITE_TEST -DSQLITE_DEBUG -DSQLITE_LOCK_TRACE -DSQLITE_FORCE_OS_TRACE -o libsqlite3.so
参考文档
http://my.oschina.net/u/587236/blog/129022
http://www.cnblogs.com/hustcat/archive/2009/03/01/1400757.html
转载于:https://www.cnblogs.com/cchust/p/4761814.html
Sqlite学习笔记(五)SQLite封锁机制相关推荐
- Struts2学习笔记(五)之异常处理机制
我们在知道在软件开发中的异常处理是很重要的,作为成熟的MVC框架的Struts2也提供了异常处理处理机制,对于一场处理:用户发送请求-->Action控制器-->发现相应的异常--> ...
- Sqlite学习笔记(四)SQLite-WAL原理(转)
Sqlite学习笔记(三)&&WAL性能测试中列出了几种典型场景下WAL的性能数据,了解到WAL确实有性能优势,这篇文章将会详细分析WAL的原理,做到知其然,更要知其所以然. WAL是 ...
- 2010年SQLite学习笔记之一
2010年SQLite学习笔记之一 一. 如何获取SQLite最新版本 官方站点:http://www.sqlite.org/ 从http://www.sqlite.org/网站 ...
- 2010年SQLite学习笔记之三
2010年SQLite学习笔记之三 一.如何备份数据库 先打开数据库test.db E:/sqlite/tool/sqlite-3_6_22>sqlite3 D:/Test/debug/tes ...
- sqlite学习笔记7:C语言中使用sqlite之打开数据库
数据库的基本内容前面都已经说得差点儿相同了.接下看看如何在C语言中使用sqlite. 一 接口 sqlite3_open(const char *filename, sqlite3 **ppDb) 打 ...
- SQLite学习笔记(七)-- 数据插入、更新和删除(C++实现)
1.代码实例 代码说明 本例主要说明如何对数据表进行插入.更新和删除操作. 测试平台 1.开发语言:C++ 2.开发工具:VS2015 3.操作系统:Win7 X64 测试数据说明 测试表为Stude ...
- SQLite学习笔记(八)-- BLOB数据的插入与查询(C++实现)
1.什么是BLOB数据 BLOB (binary large object)即二进制大对象,是一种可以存储二进制文件的容器.在计算机中,BLOB常常是数据库中用来存储二进制文件的字段类型.常见的BLO ...
- Apache Nutch 1.3 学习笔记十(插件机制分析)
1. 一些对象说明 PluginRepository:这是一个用于存储所有插件描述对象(PluginDescriptor),插件扩展点(ExtensionPoint)和被激活的插件. PluginDe ...
- Python3.7学习笔记30-垃圾回收机制
Python3.7学习笔记30-垃圾回收机制 一.前言 Python 程序在运行的时候,需要在内存中开辟出一块空间,用于存放运行时产生的临时变量:计算完成后,再将结果输出到永久性存储器中.如果数据量过 ...
最新文章
- 听说你要离开大厂去创业公司做CTO?
- 从windows server的文件服务到分布式文件服务(八)
- Error dialog box generic entry point
- 计算机最佳参数,对使用者来说,学会计算机性能参数提取,给出性能评价很重要...
- RabbitMQ路由模式(direct)
- c语言三位数倒序,C语言求助!一个三位数的逆序数,总是编不对
- ARM 与 STM32 的关系
- 无线扫码枪 服务器查询异常,扫描枪常见问题
- 关于MATLAB未定义函数或变量 ‘wavread‘的很简单的解决办法
- STM32CubeIDE 复制工程
- 激励人生成功的10句经典中英文
- 毕业设计之“真心话大冒险”小程序
- Lambda预主网接入视频教程
- ubuntu14.04 酷我音乐盒版本
- 区块链珠宝溯源,解决珠宝行业信任危机
- 双向板受力特点_单向板与双向板的受力特点
- c语言动态规划最优二叉搜索树,最优二叉搜索树(动态规划)
- 工业物联网底层数据平台 Tulip 获1300万美元A轮融资
- C语言:数组的初始化,打印,逆置(使用函数实现)。
- 环境艺术设计计算机学什么,计算机系环境艺术设计专业学生《CAD课程》学习心得...
热门文章
- (转)C# 把我所积累的类库全部分享给博友(附件已经上传)
- 持续集成与自动化部署 - jenkins sonar代码质量管理平台 部署和基础使用(五)...
- react 项目总结
- [js高手之路] 跟GhostWu一起封装一个字符串工具库-扩展字符串位置方法(4)
- 黑客攻防:关于工业网络安全的那些事
- 【转】TCP协议的无消息边界问题
- Android应用开发——onStop的调用时机
- 订单编号,递增且不连续(php版)
- 【最新版】Java速成路线(急于找工作!)
- JVM中GC Root对象有哪些?