SQLite页缓冲区管理
页面管理器是访问本地数据库文件和日志文件的唯一模块(通过操作系统API)。但是它不对数据库的内容做解析,也不对数据库内容做修改(但是页管理器会对文件头信息部分内容做修改)。它把随机访问系统或面向字节的文件系统抽象成数据库文件(基于页的随机访问系统)。它定义了一套与文件系统无关的接口来访问数据库文件的页。B+树使用页面管理模块的接口访问数据库,而非直接访问数据库文件或日志文件。B+树把数据库文件视为大小均匀的页的逻辑队列。除内存数据库外,一般的数据库都是把数据库文件存在于外存(如:磁盘)上,用的时候读入内存,操作之,有需要的话再写回外存。一般情况下,数据库文件会比较大。又因为内存有限,所以在一部分内存中只保持一小块的数据库文件,这块内存叫做database cache或data buffer,在SQLite中叫做page cache,页管理器负责管理page cache。
1 页面管理器工作职责:对于每一个数据库文件,在文件和缓冲区之间移动页面是页面管理器作为缓冲区管理器的基本功能。移动页面对B+树和其他高层模块是透明的。页面管理器是文件系统与这些高层模块的中间人,其主要目的是数据库在内存中的页是可寻址的,使得高层模块可直接访问页的内容,也负责把页写回数据库文件。页面管理器建立其了一个抽象,使得数据库文件在内存中呈现成为页的队列。除了缓冲区管理器,页面管理器也提供了其他功能,它提供了一个典型的事务处理系统所需的核心功能:事务管理器(实现事务ACID属性,并发的控制及恢复,原子的提交和回滚),数据管理器(负责协调数据库文件与缓冲区之间的页的读写,文件空间管理),日志管理器(向日志文件写入记录),锁管理器(确保事务在访问数据库页之前已经获得对数据库文件的相应的锁)。总之,页面管理器实现了存储的持久性和事务的原子性。
页面管理器的上层的模块完全从低级别的锁和日志管理机制绝缘,他们不知道锁和日志的活动。B+树模块把所有东西看作以事务为单位,不关心ACID特性的具体实现。页管理器把一个事务拆分成锁操作,日志操作,对数据库文件的读写操作。B+树以页号向页管理器请求一个页,页管理器返回一个指向已加载到缓冲区的页的内容的指针。在对页改动之前,B+树模块通知页管理器使页管理器在日志文件里保存足够的信息以应对可能的恢复,并且要获得对相应数据库文件的锁。B+树在用完一个页时通知页管理器,如果页面被更改,则写回文件。
2 页面管理器接口结构:页面管理器实现了名为Pager的数据结构,每个打开的数据库文件通过一个单独的Pager对象来管理,每个Pager对象都与唯一一个打开的数据库文件对应,数据库文件和Pager对象几乎等价。B+树想用一个数据库文件时,就新建一个Pager对象,使用对象作为句柄对文件进行页级别操作。页管理器用句柄跟踪关于文件的锁,日志文件,数据库的状态,日志状态等信息。
3 缓冲区管理:SQLitre为每个打开的数据库文件维护一个单独的page cahce(页缓冲区)。如果一个线程两次或多次打开相同的文件,页管理器只在第一次调用时创建并初始化一个单独的页缓冲区。如果两个或多个线程打开相同的文件,则对这同一个文件将会有很多独立的页缓冲区。内存数据库内存数据库不涉及任何外部设备,但是他们也被视为像普通本地文件一样,全部都存储在页缓冲区中。因此,B/B+树模块可以用相同的接口来访问任何数据库。
页缓冲区位于应用程序的内存空间。缓冲区中的页也可能被操作系统所缓存。当应用程序要读任意文件的内容时,操作系统先提供自己缓存的页,再用应用程序自己的缓冲区的页。SQLite的页缓冲区的组织和管理是独立于操作系统的。页缓冲区的管理是系统性能的关键。下面讨论页管理器如何管理和维护页缓冲区,缓冲区的客户端如何读取或修改缓冲区元素。
3.1 缓冲区组织:为了加快搜索缓存速度,内存中缓冲区中内容都会有比较好的组织。SQLite用哈希表组织已被缓存的页,用page-slots(页槽)存储表中的页。缓冲区是全相连的,即任何页槽可以存储任何页面。哈希表最初是空的,随着页需求的增加,页面管理器创建了新的页槽并把其插入哈希表中。一个缓冲区能存储的页槽有个最大值,而内存数据库中无限制(只要操作系统允许应用程序的空间增长)。
哈希表中的每一页都由一个PgHder类型的对象表示,接着是page image,接着是一些私有数据(B+树用来存储页面特定的控制信息)。(内存数据库没有日志文件,所以他们的恢复信息被记录在内存对象中,指向这些内存对象的指针存储在在私有数据之后,这些指针只被页管理器使用。)当页管理器把附加页调入缓冲区时,缓冲区中的这个页被初始化为零。在缓冲区的所有的页面都可以通过哈希表访问,在Pager对象中哈希表名为aHash,数组大小在SQLite库被编译时就已经确定。每个数组元素都指向‘一桶’页面,每桶的页面以一个无序的双向链表组织起来。PgHdr对象只是对页面管理器是可见的,但对B+树和高层模块是不可见的。PgHdr对象存储了很多控制变量:pgno表示在数据库中其代表的的页号,injournal为真表示本页已经写到回滚日志中,needSync为真表示在数据页被写回到数据库文件之前,日志需要先被刷新到磁盘上(刷新是把一个文件被改动的部分,传输到磁盘上),dirty为真表示页文件被改变,但是并没被写回到数据库文件,inStmt为真表示页在当前的语句声明中,nRef表示当前页的引用数,如果值大于0,页就是活跃的,我们说页是被钉住的,否则页是未被钉住的并且是自由的。还有很多指针变量,在PgHdr对象中,pNextHash和pPrevHash指针是把同一个哈希桶内的页面连起来,pNextStmt和pPrevStmt指针用于把在日志声明中页连起来,pNextFree和·pPrevFree指针用来把所有自由页连起来,自由页从未被拿出哈希桶,在缓冲区中所有的页面(无论是否自由页)都被pNextAll指针所连接起来,pDirty指针把所有的脏页连接起来,注意一个自由业也可能是脏页。
3.2 缓冲区读:缓冲区是以页号作为搜索键。为了读取一个页,B+树模块调用页面管理器的API sqlite3pager_get函数来读取页号。函数获取页P步骤如下:
1 搜索缓冲区空间。
(a )对P值应用哈希函数,并获得索引值(SQLite使用非常简单的哈希函数来得到索引值,页号模哈希数组大小)。
(b)用索引到aHash数组中并得到哈希桶中
(c)以遍历pNextHash指针方式,搜寻桶中。如果找到了P值,则发生缓冲命中,引用值+1,并把页地址返回给调用者。
2 如果P在缓冲区找不到的话,被认为是缓冲未命中。函数找到一个空闲槽来存放期望页(如果缓冲区没有到达最大数目限制,就会增加一个新的槽)。
3 如果没有空闲的槽可用或可以被创建,某槽就会被决定来释放当前的某一页来重用该槽。这叫做遇难者槽。
4 如果受害人槽或空闲槽是脏的,这一页就会被写回到数据库文件中去。
5 页P被从数据库文件读入到一个空闲槽,钉住该页(即引用值+1),并把该页地址返回给调用者。如果页P比当前系统中的最大页还大,是不能读取该页的,代替的,会把整个页初始化为0,还会把底部的私有数据初始化为0。SQLite严格遵循按需存取页面执行政策,使得获取页面逻辑变得简单。
3.3 缓冲区写入
当一个页的地址被返回给B+树模块时,页面管理器并不知道客户端什么时候会用这个页。SQLite遵循简单规则如下:客户端获取页,应用页,释放页。在获取到一页后,客户端能直接的修改页内容,但是一定要在作出任何修改之前先调用sqlite3pager_write这个页面管理器API。在从调用返回时,客户端能更新这个页。在写函数第一次对一个页调用时,页管理器会把原始页的内容写到回滚日志文件上去,并把injournal和needSync置位。然后,当日志记录被刷新到磁盘上后页面管理器清除needSync(SQLite支持Write Ahead Log即日志预写协议:直到needSync被清除,否则他们不会把一个已经修改过的页面写回到数据库文件)。每次写函数对一个页调用时,dirty被置位,仅当把这页写内容回到数据库文件时才会清除dirty。因为当客户端对某一页进行写操作时,页面管理器并不知道,对页面的更新并不会马上传到数据库文件。页面管理器遵从延迟写(写回)页面更新的策略。只有当页面管理器进行缓冲区刷新或者是选择性回收脏页的时候,更新才会被传播到数据库文件上去。
3.4 缓冲区替换
当缓冲区变满的时候,缓冲区替换会发生。旧的页被移出,为新页腾出空间。当有一个页请求,且这个页不在缓冲区中,并且也没有空闲的槽,页面管理器会找出一个槽当遇难者槽来替换。因为缓冲区是全相连的,所以对一个新的页来说,任何槽都是可以的。遇难者槽的确定由缓冲区替换算法决定。SQLite遵循一种类似于LRU(最近最少使用)的替换策略。SQLite组织自由页作为一个逻辑队列,当一个页被解除钉住时,页面管理器会把这个页放到队列尾部去。遇难者页将会被从队列的头部进行选择,但是不是总会选择LRU队列的首元素。SQLite试着找出在队列头的槽来回收,这样可以不涉及到往日志文件上做刷新操作。(遵守WAL协议,在把脏页写到数据库文件时,页管理器会刷新日志文件。)刷新是一个缓慢的操作,SQLite试图尽可能推迟操作时间。如果遇难者页被找到,队列最前面的页会被回收。否则,SQLite首先刷新日志文件,然后再回收队列上的首个槽。如果遇难者页是脏的,页管理器会在回收该页之前把该页写回数据库文件。被钉住的页是当前被使用的页,不能被回收。为了避免缓冲区的所有页都被钉住的情况,SQLite需要在缓冲区中有一个最小数量的槽来确保总是有一定数量的槽可以被回收,SQLite3.36默认是10个槽。
SQLite页缓冲区管理的更多相关文章
- UWP开发之ORM实践:如何使用Entity Framework Core做SQLite数据持久层?
选择SQLite的理由 在做UWP开发的时候我们首选的本地数据库一般都是Sqlite,我以前也不知道为啥?后来仔细研究了一下也是有原因的: 1,微软做的UWP应用大部分也是用Sqlite.或者说是微软 ...
- SQLite学习笔记(十)&&加密
随着移动互联网的发展,手机使用越来越广泛,sqlite作为手机端存储的一种解决方案,使用也非常普遍.但是sqlite本身安全特性却比较弱,比如不支持用户权限,只要能获取到数据库文件就能进行访问:另外也 ...
- mono for android中使用dapper或petapoco对sqlite进行数据操作
在mono for android中使用dapper或petapoco,很简单,新建android 类库项目,直接把原来的文件复制过来,对Connection连接报错部分进行注释和修改就可以运行了.( ...
- 【腾讯Bugly干货分享】微信iOS SQLite源码优化实践
本文来自于腾讯bugly开发者社区,非经作者同意,请勿转载,原文地址:http://dev.qq.com/topic/57b58022433221be01499480 作者:张三华 前言 随着微信iO ...
- 由一个bug引发的SQLite缓存一致性探索
问题 我们在生产环境中使用SQLite时中发现建表报“table xxx already exists”错误,但DB文件中并没有该表.后面才发现这个是SQLite在实现过程中的一个bug,而这个bug ...
- SQLite源程序分析之sqlite3.c
/****************************************************************************** ** This file is an a ...
- SQLite剖析之存储模型
前言 SQLite作为嵌入式数据库,通常针对的应用的数据量相对于DBMS的数据量小.所以它的存储模型设计得非常简单,总的来说,SQLite把一个数据文件分成若干大小相等的页面,然后以B树的形式来组织这 ...
- SQLite 预写式日志
SQLite在3.7.0版本引入了WAL (Write-Ahead-Logging),WAL的全称是Write Ahead Logging,它是很多数据库中用于实现原子事务的一种机制,引入WAL机制之 ...
- SQLite学习笔记(九)&&pager模块
概述 通过上一篇文章的分析,我们知道了pager模块在整个sqlite中所处的位置.它是sqlite的核心模块,充当了多种重要角色.作为一个事务管理器,它通过并发控制和故障恢复实现事务的ACID特性, ...
随机推荐
- Linux type命令
用途说明 type命令用来显示指定命令的类型.一个命令的类型可以是如下几种: alias 别名 keyword 关键字,Shell保留字 function 函数,Shell函数 builtin 内建命 ...
- QT中给各控件增加背景图片(可缩放可旋转)的几种方法
http://blog.csdn.net/liukang325/article/details/44832397 1. 给QPushButton 增加背景图片:背景图片可根据Button大小自由缩放. ...
- cocos2d/x 自带字体(label)
CCLabelTTF* label1 = CCLabelTTF::labelWithString("1掼蛋as", "AppleGothic", 15); la ...
- angularjs 迭代器
angularjs 迭代器可以使用管道字符(|)添加到表达式和指令中. 有以下五种转换数据的迭代器: (1)currency-格式化数字为货币格式. (2)filter-从数组中选择一个一个子集. ( ...
- Javascript中call函数和apply函数的使用
Javascript 中call函数和apply的使用: Javascript中的call函数和apply函数是对执行上下文进行切换,是将一个函数从当前执行的上下文切换到另一个对象中执行,例如: so ...
- datatables 前端表格插件 增删改查功能
官方网站:http://datatables.club/example/<!-- DataTables CSS -->css引入的<link rel="stylesheet ...
- rtabmap_ros安装---43
摘要: 原创博客:转载请标明出处:http://www.cnblogs.com/zxouxuewei/ 一.前言 RTAB-Map (Real-Time Appearance-Based Mappin ...
- 使用Firefox user agent进行移动端网页测试
Selenium 真是个强大的网页测试工具,设置Firefox user agent, 就可以轻松模拟手机端浏览器进行网页测试. Demo Code # -*- coding:utf8 -*- imp ...
- Python中reactor,factory,protocol
最为简单的情况下,除了了解清reactor的简单使用,你还要了解Protocol和Factory.它们最终都会由reactor的侦听建立和run来统一调度起来. 建立服务器的第一个要解决的问题就是服务 ...
- linux网络虚拟化
图解几个与Linux网络虚拟化相关的虚拟网卡-VETH/MACVLAN/MACVTAP/IPVLAN http://smilejay.com/2012/08/qemu-kvm-networking-m ...