SQLite 剖析
由于sqlite对多进程操作支持效果不太理想,在项目中,为了避免频繁读写 文件数据库带来的性能损耗,我们可以采用操作sqlite内存数据库,并将内存数据库定时同步到文件数据库中的方法。
实现思路如下:
1、创建文件数据库;
2、创建内存数据库(文件数据库、内存数据库的内部表结构需要一致);
3、在内存数据库中attach文件数据库,这样可以保证文件数据库中的内容在内存数据库中可见;
4、对于insert、select操作,在内存数据库中完成,对于delete、update操作,需要同时访问内存、文件数据库;
5、定时将内存数据库中的内容flush到文件数据库。
一、内存数据库:
在SQLite中,数据库通常是存储在磁盘文件中的。然而在有些情况下,我们可以让数据库始终驻留在内存中。最常用的一种方式是在调用sqlite3_open()的时候,数据库文件名参数传递":memory:",如:
rc = sqlite3_open(":memory:", &db);
在调用完以上函数后,不会有任何磁盘文件被生成,取而代之的是,一个新的数据库在纯内存中被成功创建了。由于没有持久化,该数据库在当前数据库连接被关闭后就会立刻消失。需要注意的是,尽管多个数据库连接都可以通过上面的方法创建内存数据库,然而它们却是不同的数据库,相互之间没有任何关系。事实上,我们也可以通过Attach命令将内存数据库像其他普通数据库一样,附加到当前的连接中,如:
ATTACH DATABASE ':memory:' AS aux1;
二、临时数据库:
在调用sqlite3_open()函数或执行ATTACH命令时,如果数据库文件参数传的是空字符串,那么一个新的临时文件将被创建作为临时数据库的底层文件,如:
rc = sqlite3_open("", &db);
或
ATTACH DATABASE '' AS aux2;
和内存数据库非常相似,两个数据库连接创建的临时数据库也是各自独立的,在连接关闭后,临时数据库将自动消失,其底层文件也将被自动删除。
尽管磁盘文件被创建用于存储临时数据库中的数据信息,但是实际上临时数据库也会和内存数据库一样通常驻留在内存中,唯一不同的是,当临时数据库中数据量过大时,SQLite为了保证有更多的内存可用于其它操作,因此会将临时数据库中的部分数据写到磁盘文件中,而内存数据库则始终会将数据存放在内存中。
sqlite分两种源码结构,一种是比较常见的sqlite3.c 一个文件十几万行代码。另一种是,将各个模块分离出的源码结构。
一、主要分为三部分:
虚拟机(Virtual Machine)
Back-end(后端)
compiler(编译器)
1、虚拟机(Virtual Machine)
2、B-tree和Pager
B-Tree使得VDBE可以在O(logN)下查询,插入和删除数据,以及O(1)下双向遍历结果集。B-Tree不会直接读写磁盘,它仅仅维护着页面(pager)之间的关系。当B-Tree需要页面或者修改页面时,它就会调用Pager。当修改页面时,pager保证原始页面首先写入日志文件,当它完成写操作时,pager根据事务状态决定如何做。B-tree不直接读写文件,而是通过page cache这个缓冲模块读写文件,对于性能是有重要意义的(这和操作系统读写文件类似,在Linux中,操作系统的上层模块并不直接调用设备驱动读写设备,而是通过一个高速缓冲模块调用设备驱动读写文件,并将结果存到高速缓冲区)。
3、编译器(Compiler)
3.1、分词器(Tokenizer)
Tokenizer.c
3.2、分析器(Parser)
SQLite的语法分析器是用Lemon(一个开源的LALR(1)语法分析器的生成器)生成的,生成的文件为parser.c。
3.3、代码生成器(Code Generator)
代码生成器是SQLite中最庞大,最复杂的部分。它与Parser关系紧密,根据语法分析树生成VDBE程序执行SQL语句的功能。
由诸多文件构成:select.c,update.c,insert.c,delete.c,trigger.c,where.c等文件。
这些文件生成相应的VDBE程序指令,比如SELECT语句就由select.c生成。
3.4、查询优化
代码生成器不仅负责生成代码,也负责进行查询优化。
主要的实现位于where.c中,生成的WHERE语句块通常被其它模块共享,比如select.c,update.c以及delete.c。
这些模块调用sqlite3WhereBegin()开始WHERE语句块的指令生成,然后加入它们自己的VDBE代码返回,最后调用sqlite3WhereEnd()结束指令生成。
B树:https://www.cnblogs.com/dongguacai/p/7239599.html
相同数量的key在B树中生成的节点要远远少于二叉树中的节点,相差的节点数量就等同于磁盘IO的次数。这样到达一定数量后,性能的差异就显现出来了。
插入或者删除元素都会导致节点发生裂变反应,有时候会非常麻烦,但正因为如此才让B树能够始终保持多路平衡,这也是B树自身的一个优势:自平衡;
B树主要应用于文件系统以及部分数据库索引,如MongoDB,大部分关系型数据库索引则是使用B+树实现。
sqlite中的锁
就数据库而言,处理的并发实际上分为读并发,写并发,读写并发,我们广义上说的并发实际上指的是读写并发,要了解这些,我们还需要先了解一下sqlite中的锁.
sqlite中一共有五种锁分别是
未加锁(UNLOCKED)
文件没有持有任何锁,即当前数据库不存在任何读或写的操作。其它的进程可以在该数据库上执行任意的读写操作。此状态为缺省状态。
共享锁(SHARED)
在此状态下,该数据库可以被读取但是不能被写入。在同一时刻可以有任意数量的进程在同一个数据库上持有共享锁,因此读操作是并发的。换句话说,只要有一个或多个共享锁处于活动状态,就不再允许有数据库文件写入的操作存在。
保留锁(RESERVED)
假如某个进程在将来的某一时刻打算在当前的数据库中执行写操作,然而此时只是从数据库中读取数据,那么我们就可以简单的理解为数据库文件此时已经拥有了保留锁。当保留锁处于活动状态时,该数据库只能有一个或多个共享锁存在,即同一数据库的同一时刻只能存在一个保留锁和多个共享锁. 需要说明的是update操作 实际上是一个读操作加一个写操作
未决锁(PENDING)
PENDING锁的意思是说,某个进程正打算在该数据库上执行写操作,然而此时该数据库中却存在很多共享锁(读操作),那么该写操作就必须处于等待状态,即等待所有共享锁消失为止,与此同时,新的读操作将不再被允许,以防止写锁饥饿的现象发生。在此等待期间,该数据库文件的锁状态为PENDING,在等到所有共享锁消失以后,PENDING锁状态的数据库文件将在获取排他锁之后进入EXCLUSIVE状态。
排它锁(EXCLUSIVE)
在执行写操作之前,该进程必须先获取该数据库的排他锁。然而一旦拥有了排他锁,任何其它锁类型都不能与之共存。因此,为了最大化并发效率,SQLite将会最小化排他锁被持有的时间总量。
读操作锁变化
了解了sqlite的锁之后 我们再来看针对读写操作,sqlite内部的锁变化
读操作的目的是获取共享锁shared从而来访问数据 那么 在获得共享锁(SHARED)之前
首先检查是否有排它锁(EXCLUSIVE) 如果有 则说明sqlite正在进行写入操作 为保障数据一致 所以无法获取共享锁(SHARED)
如果没有 再检查是否有未决锁(PENDING)如果有 表示当前有准备进行的写操作并阻止共享锁(SHARED)的获取
如果检测不到上述两个锁 将获得共享锁(SHARED) 读取数据 然后释放共享锁
写操作锁变化
对于写操作,写操作的目的是为了获得 排它锁(EXCLUSIVE) 独占数据库从而一致性,其内部锁变化如下
首先 检查数据库是否有保留锁(RESERVED)与排它锁(EXCLUSIVE) 如果有 则说明在此次写操作之前还准备有或者正在进行一次写操作
此时如法获取排它锁(EXCLUSIVE) 如果没有 则获取未决锁(PENDING) 当未决锁(PENDING)获得时,将无法再获取到共享锁(SHARED),也就是说sqlite此时已经不再处理读请求
在持有未决锁(PENDING)期间 将会不断询问内部是否还有共享锁(SHARED)
当等待所有共享锁(SHARED)消失 当所有共享锁(SHARED)消失时 此时锁状态将由未决锁(PENDING)切换至排它锁(EXCLUSIVE)
并写入数据 当排它锁(EXCLUSIVE)激活时 阻止任何类型的其它锁获取 直至写入完毕并释放排它锁(EXCLUSIVE)
总结
最后我们来总结一下
1.当有写操作时,其他读操作会被驳回
2.当有写操作时,其他写操作会被驳回
3.当开启事务时,在提交事务之前,其他写操作会被驳回
4.当开启事务时,在提交事务之前,其他事务请求会被驳回
5.当有读操作时,其他写操作会被驳回
6.读操作之间能够并发执行
--------------------- 本文来自 https://blog.csdn.net/zhangsheng_1992/article/details/52598396?utm_source=copy
SQLite 剖析的更多相关文章
- SQLite剖析之存储模型
前言 SQLite作为嵌入式数据库,通常针对的应用的数据量相对于DBMS的数据量小.所以它的存储模型设计得非常简单,总的来说,SQLite把一个数据文件分成若干大小相等的页面,然后以B树的形式来组织这 ...
- SQLite剖析之设计与概念
1.API 由两部分组成: 核心API(core API)和扩展API(extension API). 核心API的函数实现基本的数据库操作:连接数据库.处理SQL.遍历结果集.它也包括一些实用函数, ...
- SQLite剖析之事务处理技术
前言 事务处理是DBMS中最关键的技术,对SQLite也一样,它涉及到并发控制,以及故障恢复等等.在数据库中使用事务可以保证数据的统一和完整性,同时也可以提高效率.假设需要在一张表内一次插入20个人的 ...
- SQLite剖析之编程接口详解
前言 使用过程根据函数大致分为如下几个过程: sqlite3_open() sqlite3_prepare() sqlite3_step() sqlite3_column() sqlite3_fina ...
- SQLite剖析之异步IO模式、共享缓存模式和解锁通知
1.异步I/O模式 通常,当SQLite写一个数据库文件时,会等待,直到写操作完成,然后控制返回到调用程序.相比于CPU操作,写文件系统是非常耗时的,这是一个性能瓶颈.异步I/O后端是SQLit ...
- SQLite剖析之动态内存分配
SQLite通过动态内存分配来获取各种对象(例如数据库连接和SQL预处理语句)所需内存.建立数据库文件的内存Cache.保存查询结果. 1.特性 SQLite内核和它的内存分配子系统提供以下特性 ...
- SQLite剖析之锁和并发控制
在SQLite中,锁和并发控制机制都是由pager.c模块负责处理的,用于实现ACID(Atomic.Consistent.Isolated和Durable)特性.在含有数据修改的事务中,该模块将确保 ...
- SQLite剖析之临时文件、内存数据库
一.7种临时文件 SQLite中,一个数据库由单个磁盘文件构成,简化了SQLite的使用,因为移动或备份数据库只要拷贝单个文件即可.这也使得SQLite适合用作应用程序文件格式.但是,当在单个文 ...
- SQLite剖析之数据类型
许多SQL数据库引擎(除SQLite之外的各种SQL数据库引擎)使用静态.严格的数据类型.对于静态类型,一个值的数据类型由它的容器,即存储这个值的列来决定.SQLite则使用更加通用的动态类型系统.在 ...
- SQLite剖析之C/C++接口
前言 SQLite3是SQLite一个全新的版本,它虽然是在SQLite2的代码基础之上开发的,但是使用了和之前的版本不兼容的数据库格式和API.SQLite3是为了满足以下的需求而开发的:支持UTF ...
随机推荐
- win10常用命令和设置总结
1.常用命令 exit:退出cmd面板; cls:清除cmd面板; 2.常用设置 2.1 services.msc 禁用:以后怎样都不会运行;手动:是打开某些用到它的程序要用到该服务时才会运行; 自动 ...
- try....fail....catch...Assert 模式的测试, fail是Junit中的功能
try { // 反射读取properties文件 new BufferedReader(new FileReader(myConfigPath[4])); //上面没有抛出异常就是执行fail, / ...
- go build 和 go install
环境:Win10 + GO1.9.2 1.区别 ①go build:编译go源码生成一个可执行文件:使用-o参数可以指定生成的可执行文件名称,如go build -o test.exe ②go ins ...
- css特效博客
1. 前端网上: e344657992 http://www.qdfuns.com/notes/15477/02cb463c28d9fe69ee0bc804448b8316.html
- C#读取text内容并且于testbox中展现 保留换行实现方法
直接上代码 //新建一个储存的list List<string> listLines = new List<string>(); StreamReader sr = new S ...
- MySQL 8.0 安装时 Authentication Method
1. 使用强密码加密授权(推荐) 8.0提供的心的授权方式,采用SHA256基础的密码加密方法. 但是需要新版本的connector,目前和Connector/J最新的时8.0 目前8.0的连接器和使 ...
- word中的交叉引用
分别使用“交叉引用”依次插入所需应用文献编号范围的第一个和最后一个. 所需引用处出现“[1][3]” 在引用处对两个编号操作:点击鼠标右键-选择“切换域代码”. [1]变为” {REF _Ref4 ...
- 20165303学习基础和C语言基础调查
20165303学习基础和C语言基础调查 技能学习心得 我认为我的乒乓球打的还不错,不能说非常好,但是基本的一些技巧都还是会的,小时候爸爸就非常爱看乒乓球比赛,有时候也带着我一起看,最开始看的时候我发 ...
- MP4介绍与基本AVC编码(x264)教程
MP4介绍与基本AVC编码(x264)教程(最后更新: 2006.03.25)为日益增加的对MP4 H264/AVC编码的需求,本人做了一个简单的MP4介绍与基本AVC编码(使用x264)教程最后更新 ...
- Artem and Array CodeForces - 442C (贪心)
大意: 给定序列$a$, 每次任选$a_i$删除, 得分$min(a_{i-1},a_{i+1})$(无前驱后继时不得分), 求最大得分. 若一个数$x$的两边都比$x$大直接将$x$删除, 最后剩余 ...