SQLite学习笔记(六)&&共享缓存
介绍
通常情况下,sqlite中每个连接都会一个独立的pager对象,pager对象中管理了该连接的缓存信息,通过pragma cache_size指令可以设置缓存大小,默认是2000个page,每个page是1024B。这样导致了对于同一个数据文件,多个连接各自维护了自己的一份缓存,在高并发情况下,可能导致使用大量的内存。而sqlite作为一个嵌入式数据库,通常用于嵌入式设备,内存可能比较有限,为了应对这种问题,sqlite提供了一种方法,通过让多个连接公用一个pager对象,共享同一份缓存。
当打开这个特性后,多个连接可以共享一个pager对象,这样在一定程度上减少了内存的消耗和文件的IO次数。一个线程的多个连接对象,以及多个线程的多个连接对象都可以采用这种方式来共享缓存。由于sqlite以动态库方式嵌入在应用程序中,每个应用程序有自己独立的进程空间,因此该特性不能用于进程之间,同一个进程可以共享一份缓存,进程之间各自维护自己的缓存。关于缓存的实现后面会单独写一篇文章。下图展示了启用共享缓存后的结构图。
图1
从图1中可以看到,Process1中的两个连接共享BtShared对象,BtShared对象对应一个Pager对象,而缓存由Pager对象管理,因此整个Process1中的所有连接公用一个缓存,通过Pager模块操作Page,IO操作对上层模块透明。
实现原理
调用接口sqlite3_open_v2打开连接时,通过指定参数SQLITE_OPEN_SHAREDCACHE来声明连接采用共享缓存模式。之前SQLite系列(二):常规性能测试 中的单表主键查询测试章节中提到,开启共享缓存模式下,导致应用的程序的并发性能大大下降,多线程情况下,CPU也只能用一个核。因此大家在实际使用中,要权衡内存和并行度,来确定是否开启共享缓存模式。
我们的测试场景都是只读,理论上不应该存在并发冲突,那么为什么不能并行?这里要看共享缓存的实现了。从图1中也可以看到,每个连接有一个btree对象,多个btree对象通过共享BtShared对象来共享缓存,BtShared对象通过mutux来维护里面的成员,包括page cache的管理,table-lock信息等,因此这个mutex是一个热点,在高并发场景下,多个线程同时访问BtShared对象,会由于竞争mutex,无法充分并发,导致并行度差。
table-lock
默认情况下,sqlite只通过文件锁就可以实现读写互斥,读读并发的效果,关于这点我在SQLite系列(五):SQLITE封锁机制中已经说明。那么为什么要引入table-lock?这个也是拜共享缓存所赐。共享缓存模式下,多个btree对象对应同一个BtShared对象,在执行更新时,首先会修改pager中cache,此时更新事务加了reserved-lock,与读事务的shared-lock不互斥。为了避免读到脏页,在共享缓存模式下,增加了table-lock,避免读写事务同时访问同一个cache,导致脏读的情况发生。用户访问具体某个表的page之前,会首先调用sqlite3BtreeLockTable对该表设置一个READ-LOCK(select 操作)或WRITE-LOCK(DML操作),如果发现冲突,则抛出SQLITE_LOCKED错误。通过这种方式,保证了多个线程不会同时对一个表进行读写操作。函数sqlite3BtreeLockTable部分实现如下:
sqlite3BtreeEnter(p); //判断加锁是否冲突
rc = querySharedCacheTableLock(p, iTab, lockType);
if( rc==SQLITE_OK ){
//加锁
rc = setSharedCacheTableLock(p, iTab, lockType);
} sqlite3BtreeLeave(p);
从上面的代码可以看到,若加锁冲突,则直接报SQLITE_LOCKED错误;否则加上锁。注意看到判断加锁和加锁过程通过BtShared对象的mutex来保护(sqlite3BtreeEnter,sqlite3BtreeLeave),因此加锁过程是串行的,table-lock链表不会被多个线程同时操作。
querySharedCacheTableLock代码逻辑
/* If some other connection is holding an exclusive lock, the
** requested lock may not be obtained.
*/
if( pBt->pWriter!=p && (pBt->btsFlags & BTS_EXCLUSIVE)!=0 ){
sqlite3ConnectionBlocked(p->db, pBt->pWriter->db);
return SQLITE_LOCKED_SHAREDCACHE;
}
setSharedCacheTableLock逻辑
for(pIter=pBt->pLock; pIter; pIter=pIter->pNext){
if( pIter->iTable==iTable && pIter->pBtree==p ){
pLock = pIter;
break;
}
}
/*create a table lock*/
if( !pLock ){
pLock = (BtLock *)sqlite3MallocZero(sizeof(BtLock));
if( !pLock ){
return SQLITE_NOMEM;
}
pLock->iTable = iTable;
pLock->pBtree = p;
pLock->pNext = pBt->pLock;
pBt->pLock = pLock;
}
遍历BtShared对象中已有锁链表,比较iTable和对应的pBtree是否与自身相同(自己是否已经加过),若没有,则申请锁对象,加入链表。
加锁流程
我们知道sqlite有两种日志模式,默认的DELETE模式和WAL模式,下面我会介绍开启共享缓存模式后,更新操作的加锁流程,主要变化在于table-lock。
普通日志模式+共享缓存模式
- 开启事务:shared-lock[sqlite3BtreeBeginTrans]
- DML操作:
- 文件锁,reserved-lock
- table-lock,
将对应的表加锁,同一个表的读锁与写锁互斥 - 读取表对应的page
- 提交:
- 加execlusive-lock [sqlite3PagerCommitPhaseOne,刷日志]
- 删除日志文件
- 释放execlusive-lock
- 释放table-lock
WAL日志模式+共享缓存模式
- 开启事务,shared-lock[sqlite3BtreeBeginTrans]
- DML操作
- 数据文件锁,shared-lock
- wal日志文件write-lock
- table-lock
- 提交
- 释放table-lock
- 释放日志文件write-lock
- 释放数据文件shared-lock
SQLite学习笔记(六)&&共享缓存的更多相关文章
- Sqlite学习笔记(四)&&SQLite-WAL原理
Sqlite学习笔记(三)&&WAL性能测试中列出了几种典型场景下WAL的性能数据,了解到WAL确实有性能优势,这篇文章将会详细分析WAL的原理,做到知其然,更要知其所以然. WAL是 ...
- Sqlite学习笔记(四)&&SQLite-WAL原理(转)
Sqlite学习笔记(三)&&WAL性能测试中列出了几种典型场景下WAL的性能数据,了解到WAL确实有性能优势,这篇文章将会详细分析WAL的原理,做到知其然,更要知其所以然. WAL是 ...
- SQLite 学习笔记
SQLite 学习笔记. 一.SQLite 安装 访问http://www.sqlite.org/download.html下载对应的文件. 1.在 Windows 上安装 SQLite. ...
- SQLite学习笔记(七)&&事务处理
说到事务一定会提到ACID,所谓事务的原子性,一致性,隔离性和持久性.对于一个数据库而言,通常通过并发控制和故障恢复手段来保证事务在正常和异常情况下的ACID特性.sqlite也不例外,虽然简单,依然 ...
- java之jvm学习笔记六-十二(实践写自己的安全管理器)(jar包的代码认证和签名) (实践对jar包的代码签名) (策略文件)(策略和保护域) (访问控制器) (访问控制器的栈校验机制) (jvm基本结构)
java之jvm学习笔记六(实践写自己的安全管理器) 安全管理器SecurityManager里设计的内容实在是非常的庞大,它的核心方法就是checkPerssiom这个方法里又调用 AccessCo ...
- Learning ROS for Robotics Programming Second Edition学习笔记(六) indigo xtion pro live
中文译著已经出版,详情请参考:http://blog.csdn.net/ZhangRelay/article/category/6506865 Learning ROS for Robotics Pr ...
- Typescript 学习笔记六:接口
中文网:https://www.tslang.cn/ 官网:http://www.typescriptlang.org/ 目录: Typescript 学习笔记一:介绍.安装.编译 Typescrip ...
- python3.4学习笔记(六) 常用快捷键使用技巧,持续更新
python3.4学习笔记(六) 常用快捷键使用技巧,持续更新 安装IDLE后鼠标右键点击*.py 文件,可以看到Edit with IDLE 选择这个可以直接打开编辑器.IDLE默认不能显示行号,使 ...
- Go语言学习笔记六: 循环语句
Go语言学习笔记六: 循环语句 今天学了一个格式化代码的命令:gofmt -w chapter6.go for循环 for循环有3种形式: for init; condition; increment ...
随机推荐
- 由项目浅谈JS中MVVM模式
文章版权由作者李晓晖和博客园共有,若转载请于明显处标明出处:http://www.cnblogs.com/naaoveGIS/. 1. 背景 最近项目原因使用了durandal.js和knock ...
- 我的Node.js处女作
前言 很高兴我的node.js处女作开发完成了,目前还在优化完善阶段,经历两周的紧张沟通和开发,工作总算搞一段落.选用node.js 一是因为这次的业务逻辑相对来说简单想拿node练练手,二就是相对来 ...
- Parallel并行编程初步
Parallel并行编程可以让我们使用极致的使用CPU.并行编程与多线程编程不同,多线程编程无论怎样开启线程,也是在同一个CPU上切换时间片.而并行编程则是多CPU核心同时工作.耗时的CPU计算操作选 ...
- iis配置出现处理程序“PageHandlerFactory-Integrated”在其模块列表中有一个错误模块“ManagedPipelineHandler”问题
最近重新由于一些问题要安装vs2015和oracle所以就重装了电脑,吧项目发布到本地的iis上的时候出现问题了, 像之前出现的问题一般都是重装电脑后,安装vs的时候,是先安装了vs,之后再Windo ...
- 【C#】 异常处理
异常是在程序执行期间出现的问题.C# 中的异常是对程序运行时出现的特殊情况的一种响应,比如尝试除以零等异情况. 异常提供了一种把程序控制权从某个部分转移到另一个部分的方式. C# 异常处理时建立在四个 ...
- css遮罩代码(已验证)
#mask { background-color: rgb(0, 0, 0); display:none; opacity: 0.0; /* Safari, Opera */ -moz-opacity ...
- DES加密中文乱码问题的解决
服务器向客户端返回时: response.setContentType("text/json; charset=utf-8"); 客户端解码时: return new String ...
- Linux安装JDK1.7
发表此篇文章纯属本人愚钝,希望以后再安装JDK不要走那么多曲折的路,也希望可以给后人借鉴. 1.以下以JDK1.7为例 具体官网地址:http://www.oracle.com/technetwork ...
- java多线程-线程池
线程池(Thread Pool)对于限制应用程序中同一时刻运行的线程数很有用.因为每启动一个新线程都会有相应的性能开销,每个线程都需要给栈分配一些内存等等. 我们可以把并发执行的任务传递给一个线程池, ...
- Dependency management
Play’s dependency management system allows you to express your application’s external dependencies i ...