SQLite占用资源少原因
本篇承接上篇SQLite详解的下篇,介绍SQLIte为什么占用资源少的原因?本文主要参考https://blog.csdn.net/hanyingzhong/article/details/46400803。
SQLite的一个显著的特点就是占用内存量很小,这作为一个嵌入式的DBMS是非常重要的,那么我下面就对这个问题从根本上分析它是如何做到“小内存”的。
一、ORDER BY查询中内存使用情况
由于SQLite的执行都是先把SQL语句转化成指令再执行,所以下面就先一条条的分析一下它所用到的指令。先建立一个表再插入下面的数据,然后再用explain操作来得到那些指令。下面就是它们的具体操作以及解释。
Sqlite> create table stu (sno int, name text, sex text, age int);
Sqlite > insert into stu values (1,'aa','n', 21);
Sqlite > insert into stu values (2,'bb','m', 18);
Sqlite > insert into stu values (3,'yy','m', 17);
Sqlite > insert into stu values (4,'xx','n', 19);
Sqlite > insert into stu values (5,'ee','n', 20);
Sqlite > insert into stu values (5,'ee','n', 24);
Sqlite > insert into stu values (6,'fe','m', 34);
Sqlite > explain select * from stu order by age;
下面是制作一个record的过程。
下面是record5的数据结构,也就是执行第十条指令后的结果:(记录1)
Hdr-size |
Int |
Text |
Text |
Int |
Sno |
Name |
Sex |
age |
再执行11-14条指令后得到的结果为:(记录2)
Hdr-size |
Int |
Text |
Text |
age |
sequence |
Record5 |
再执行第15条指令是将最后得到的一个record插入到临时数据库文件1中,也就是执行Open Ephemeral后得到的数据文件。就象上面一直循环将所有要排序的数据插入到临时数据库中。
再执行第19条指令,打开的是个临时表,这个表的特点就是这个表中只有一条记录,每当插入第二条记录第一条就会自动被删除,这样可以节约内存的使用。
再执行第20条指令,循环遍历临时数据库文件1的数据。
再执行第21条指令,这是从数据库1中得到第3列放到amem[]第5个位中,也就是取出上面记录2中的Record5值,也就是记录1,放到第五位。
再执行第22条指令,它是把一个整数值(代表一个键值)放到第九位。
再下来执行第23条指令,它是将amem第5位中的数据(记录1)和第9位的键值取出来插入到表2中,此时得到的数据就是原来的数据。
再下来的指令就是从这条指令中把数据取出做下面的工作,比如输出或者是计数等操作。
这里实现排序功能的就是IdxInsert指令和,因为它是将得到的带有排序关键字的记录插入到B树的合适位置,再执行第21条指令的时候一条条的把数据取出来,这样就是有序的数据了。
而对于内存的使用来说,也就是在这个问题上,因为其它操作都是在计算,不用什么内存的,而它用内存就是看它是把数据存放到什么地方了,很明显它是存在数据库1中的,那么问题就是说数据库1的建立是的是在磁盘上还是内存中,也就是指令OP_OpenEphemeral的执行情况:
allocateCursor();//先给它分配游标
再下来创建数据库:
sqlite3BtreeFactory (db, 0, 1, SQLITE_DEFAULT_TEMP_CACHE_SIZE, openFlags, &pCx->pBt);
在这个函数中会判断它是创建什么类型的数据库,这里是用第二个参数决定类型的,如果不为空且是个具体的名字,那么就调用sqlite3BtreeOpen()函数打开即可,如果是":memory:",那么直接在内存中建立,如果为0,说明就是虚数据库,可以是在内存也可以是数据库文件,这会决定于两个条件:
SQLITE_TEMP_STORE和db->temp_store==2,下面是它们的决定方式:
这说明这个排序也不会给内存使用带来很大的影响。
下面再根据代码分析它执行IdxInsert的内存使用情况:
它也是执行函数:sqlite3BtreeInsert(pCrsr, zKey, nKey, "", 0, 0, pOp->p3);只是它在这里不插入数据只插入关键字Key,这个zKey其实就是上面所说的记录2,执行这个函数过程中,首先它申请临时空间,调用函数allocateTempSpace(pBt),这个操作申请到的空间一般是固定的,为1024B,也就是一个页面的大小,随着page_size变化。这1K内存就是它执行排序时比其它不排序的多出来的内存使用量。但是这个TempSpace对于一个Btshared对象来说只有一个,当申请后再就不能申请了。
上面那个插入操作函数再调用fillInCell(),这里是真正的将数据插入到临时数据库中的操作,首先是将数据插入到申请到的临时内存空间中,如果空间用完就再从磁盘中申请页面来存储溢出数据。
二、下面再具体算一下总共所用的3M内存用到何处了。
1) 首先打开一个数据库自动要分配2000个页面,也就是2000K
2) 再下来执行指令Open Ephemeral,内存分配情况如下图:
3) 再就是OpenRead指令:这里也是为主数据库分配一个游标,大小为300K
4) 再下来执行Idxinsert要分配1024B的临时内存空间
5) 执行OpenPseudo指令打开一个游标也要300B的空间
共使用2502K空间
从上面可以看出,其实内存空间的应该只是它自身的一些初始分配,对于这个操作,额外的应用是很少的,2502K这个数和经测试的3M也相差不多。
三、再下来讨论一下Pager是如何来管理页面的
在打开数据库的时候首先会把Pager的页面数设置成2000个,执行函数sqlite3_open()的时候它会调用openDatabase()函数,再调用sqlite3BtreeFactory(),这个函数的一个参数在这里是用了一个默认值SQLITE_DEFAULT_CACHE_SIZE,它是2000,这是Pager的最大的页面数,再通过sqlite3BtreeSetCacheSize(*ppBtree, nCache)设置这个最大页面数,接下来应用内存的操作是在遍历数据库的时候,首先执行指令OP_Rewind,它会找到要查询的B树的第一个记录,也同时把第一个页面调入内存,通过执行getAndInitPage(pBt, pCur->pgnoRoot, &pCur->apPage[0])操作,这样再继续执行下面的指令来取数据以及对数据进行处理,等到循环第二次的时候执行OP_next指令,它其实是要找到下一条数据,首先判断此数据的索引值是否可以在现在游标所处的页面的内存单元之外,如果是那么说明在这个页面中已经找不到,那么就再在内存中找这个页面,因为这个页面有可能因为以前的操作已经把它放入内存中了,如果能找到就返回这个页面指针,如果找不到的话再去已经闲置不用的队列中找合适的页面来用,如果能找到就用这个,如果找不到就再新建一个页面。
那么再下来对上面得到的页面进行初始化:数据就是在数据库文件中到需要的页面并且将这个页面的数据读到刚才得到的新页面中,这样就完成了一次数据的搜索,每次执行OP_next指令的操作都是这样,如此反复直到读完数据为止……
四、多表连接的内存使用情况分析
多表连接的做法已经在前面查询优化中讲过了,至于它的内存使用其实和普通的查询基本是一样的,只不过是多了几个游而已,有几个表连接就打开几个对应的游标,再另加一个用于输出的游标,所以内存和普通查询基本相同,这里不再叙述。
五、建立索引的内存使用情况。
索引的建立是将数据生成关键字再把它们插入到B树的合适位置中去,B树是存储在文件中的,所以使用内存的数量和前面讨论过的排序中执行指令idxinsert是相同的,所以内存使用也是很小,这里不再叙述。
六、插入操作的内存使用情况
插入操作分两种情况,一种就是自动提交,这样就是执行一次就提交一次,这个过程中,存在内存中的数据量不会很大,所以插入操作使用内存情况就是一条数据的大小。
而如果是将插入放到BEGIN和COMMIT语句之间的话,那么这就是手动提交,中间执行的操作都会保存在内存中,插入多少条保存多少的数据,这很明显占内存量就是很大了。与在这两个语句中插入的数据量成正比。
七、更新操作的内存使用情况
更新操作使用内存比较大,因为在SQLite中,更新会采用两个步骤进行,第一步先是把满足条件的数据找出来存到一个RowSet的内存单元中,对数据遍历完一遍后再将这些数据从RowSet中取出来,再一条条的更新,所以数据多的话使用内存就会很大,其中放到RowSet中的指令为OP_RowSetAdd,它会执行一个插入函数sqlite3RowSetInsert(),在这个函数中要对内存空间进行管理,如果还有空间就继续插入,没有空间就再申请。
八、删除操作的内存使用情况
删除操作使用内存比较大,因为在SQLite中,删除会采用两个步骤进行,第一步先是把满足条件的数据找出来存到一个RowSet的内存单元中,对数据遍历完一遍后再将这些数据从RowSet中取出来,再一条条的删除,所以数据多的话使用内存就会很大,其中放到RowSet中的指令为OP_RowSetAdd,它会执行一个插入函数sqlite3RowSetInsert(),在这个函数中要对内存空间进行管理,如果还有空间就继续插入,没有空间就再申请。
这一篇讲到B树在SQLite中的使用,使得SQLite内存占用资源较少,这也引出了B树的数据结构(建树,插入,删除)的操作,下一篇讲述B树的操作。
SQLite占用资源少原因的更多相关文章
- sql server 2000数据库 最近经常出现某进程一直占用资源,阻塞?死锁?
OA的数据库最近多次出现某进程一直占用资源,导致其他进程无法执行.使用sp_who2 和 sql server profiler跟踪查询,发现有以下几个语句常常占用资源: 1.declare @P1 ...
- WaitForSingleObject与WaitForMultipleObjects用法详解(好用,而且进入一个非常高效沉睡状态,只占用极少的CPU时间片)
在多线程下面,有时候会希望等待某一线程完成了再继续做其他事情,要实现这个目的,可以使用Windows API函数WaitForSingleObject,或者WaitForMultipleObjects ...
- Ubuntu 使用top/free查看内存占用大的原因
Ubuntu 使用top/free查看内存占用大的原因 linux/ubuntu下free/top查看内存占用大的原因 使用free/top查看内存占用的时候,吓了一大跳,机器4GB的内存,显 ...
- Android高效内存:让图片占用尽可能少的内存
Android高效内存:让图片占用尽可能少的内存 一.让你的图片最小化 1.1 大图小图内存使用情况对比 大图:440 * 336 小图:220 * 168 小图的高宽都是大图的1/2--> ...
- Android高效内存2:让图片占用尽可能少的内存
Android高效内存:让图片占用尽可能少的内存 一.让你的图片最小化 1.1 大图小图内存使用情况对比 大图:440 * 336 小图:220 * 168 资源目录:xhdpi 小图的高宽都是 ...
- Windows下找到JVM占用资源高的线程
与linux下top命令直接显示进程下线程资源占用不同,Windows下默认任务管理器只能显示出进程的资源占用,jconsle等工具也只能显示出java进程资源占用,无法显示出进程能具体线程的资源占用 ...
- oracle查询最占用资源的查询
从V$SQLAREA中查询最占用资源的查询 select b.username username,a.disk_reads reads,a.executions exec,a.disk_reads/d ...
- SQL SERVER 占用资源高的SQL语句
--SQL SERVER 占用资源高的SQL语句: --查询占用cpu高的前 50 个 SQL 语句 SELECT total_cpu_time,[total_physical_Reads], tot ...
- 解决update-apt-xapi占用资源过高的问题
最近云主机出现了个报错,查看系统日志发现是update-apt-xapi任务占用资源过高,甚至内存占完了无法开辟内存 云主机:Ubuntu 14.04.5 LTS update-apt-xapi是干嘛 ...
随机推荐
- mysql 循环写入数据库
测试过程经常用到插入数据 我们首先建一个函数: delimiter # create procedure test_double() begin declare i int default 0; de ...
- Linux系统调用:进程的终止
之前总结了Linux的系统创建,主要是fork()函数和vfork()函数,最近总结了Linux进程的终止,主要的调用是_exit()和exit(). 先看看两个函数的原型以及各自属于的头文件,可以发 ...
- linux使用framebuffer的代码
#include <linux/fb.h> #include <sys/mman.h> #include <sys/ioctl.h> #include <st ...
- vector作为二维数组
vector本来就是可以用来代替一维数组的,vector提供了operator[]函数,可以像数组一样的操作,而且还有边界检查,动态改变大小. 这里只介绍用它来代替二维的数组,二维以上的可以依此类推. ...
- xpath爬取新浪天气
参考资料: http://cuiqingcai.com/1052.html http://cuiqingcai.com/2621.html http://www.cnblogs.com/jixin/p ...
- oracle数据导入
1.删除原有数据库的内容 drop user username cascade; 我的数据库名为test,所以sql语句为: drop user test cascade; 2.创建表空间: 语句为: ...
- HTML 通过js实现div的拖动效果
最近做项目,碰到一个问题,需要对div实现拖动效果. 在度娘找了很多,要么觉得代码太长,要么就是效果不理想,不过最后还是找到了一个不错的,感谢大神的留贴,方便了我们,就把代码贴下面了: <!DO ...
- 删除坏掉的 Active Directory Domain
最近公司的某个 Domain Controller 报告可能由于长时间没在线,同步失败,然后用 Repldiag 工具清理 lingering objects 的过程中,该工具报告存在 serious ...
- iis发布后模板字体不能加载的解决方案
在使用ace模板的过程中就曾遇到过图标不显示的情况, 1.在iis和vs运行都不能显示图标,添加缺失的字体库后可以访问 2.把项目签入到阿里云时再一次失效,解决方法是添加Mime类型 .woff a ...
- jQuery.extend(object)
为jQuery类添加类方法,可以理解为添加静态方法. jQuery.extend({ min: function(a, b) { return a < b ? a : b; }, max: f ...