SQLite中字段顺序和PAGE_SIZE对性能的影响
1.背景
SQLite数据库中有1张表,该表含若干个字段,其中有1个字段为BLOB类型,且BLOB字段不是最后1个字段。表结构类似如下(col3为BLOB字段):
T (col1 INTEGER,col2 TEXT,col3 BLOB,col4 REAL,col5 TEXT)
业务系统要遍历这张表的内容,但查询内容不包括BLOB字段,即查询SQL类似如下:
Select col1,col2,col4,col5 from T;
2.问题说明
上述的使用模式,在表T较小的情况下运转尚且良好,但当表T较大时(在我们的系统中.DB文件达到了100G,且BLOB占了主要的存储),遍历一次表需要很长的时间,长达几个小时。那么对于这样的使用场景,应该要如何去优化呢?
3.优化思路
若不考虑物理IO优化和操作系统优化,仅考虑DB优化,一般来说,优化无外乎如下几种常用的方式:
- 索引。但对于需要进行遍历访问的表,通过索引显然毫无优化空间,甚至会效率更低。因此索引的优化思路首先被放弃。
- SQL优化。但这个SQL,属于最基本的查询SQL,因此也没有优化空间。
- 并行查询。将之前1个进程访问所有的记录,改为多个进程分别访问不同的记录区间。这种方式可以尝试。
- 参数优化。例如通过设置PAGE_SIZE参数,调整最小存储单元PAGE的大小。
- 其它优化。基于DB文件格式和数据库运行原理进行优化。
根据以上描述,下文我将从并行查询、参数优化、其它优化3个方面进行优化实验。
4.优化实验
4.1.并行查询
我将表T的所有记录,按ROWID每5000条作为一个单元,然后开启多个进程分别查询不同的单元。通过这种方式的确实现了并行,但单个进程的IO吞吐会随进程数的增加而减小,使总体的性能未有提升,下表是开启不同个数的并行进程时,各进程获得的IO吞吐量:
|
并行进程个数 |
各进程的IO吞吐 |
全部执行完毕耗时 |
|
1 |
18M/s |
约2小时 |
|
4 |
4.4M/s |
约2小时 |
|
6 |
2.9M/s |
约2小时 |
|
8 |
2.2M/s |
约2小时 |
尽管可以实现多个进程同时工作,但对于SQLite来说,并行并没有扩展IO的吞吐能力。因此并行查询,不能起到优化效果。
4.2.参数优化
SQLite可通过PRAGMA宏来设置不同的运行参数。通过分析所有可被设置的参数,我认为PAGE_SIZE参数可能会较明显地影响优化效果。基于如下分析:
PAGE是SQLite的最小存储单元,它是表扩张和收缩的基本单位,表中的记录都存储在PAGE中(类似于ORACLE中的block)。PAGE_SIZE用来指定PAGE的大小,不同版本有不同的默认值(v3.12之前是1024 Byte,v3.12之后是4096 Byte),改变默认值只能在创建.DB中第1张表之前进行(或改变默认值之后立刻执行VACUUM)。
我们的业务系统使用的数据库版本小于v3.12,因此其默认的PAGE_SIZE为1KB,而通过分析数据,发现几乎表中所有的记录,其大小均大于1KB,甚至达到几百KB。在PAGE中的存储表记录时(在SQLite中也称为payload),会首先使用当前PAGE中剩余的存储空间,当剩余空间不够用时,会产生一个overflow page(溢出页),然后继续在溢出页中存储payload剩余的内容,空间仍然不够用时,会继续产生溢出页,以此种方式直到将payload表达完整。其示意图如下:

假设一条记录为3K,当PAGE_SIZE为1K时,完全查询这一条记录需要3次寻址(查找对应的PAGE);而当PAGE_SIZE为4K,完全查询这一条记录仅需1次寻址。
制作了一个测试表,平均记录大小为76KB,一共有12000条记录,表大小约960M,设置不了不同的PAGE_SIZE,其查询效率的对比如下:
|
PAGE_SIZE |
该使用场景查询耗时 |
|
1K |
4.93s |
|
2K |
2.74s |
|
4K |
1.67s |
|
8K |
1.08s |
|
16K |
0.79 |
根据上表可知,当表记录较大时(超过PAGE_SIZE的大小),随着PAGE_SIZE的增大,本使用场景的查询耗时越小。查询效率提升的倍数大致与PAGE_SIZE的倍数一致。
4.3.其它优化
通过分析SQLite的文件格式可知,表记录的所有字段的内容是连续排列的,这与ORACLE等数据库是不同的(ORACLE对于LOB对象,仅在字段内容中记录LOB的地址,而非实际LOB内容)。差别如下图:

对于SQLite,如果要查询col4和col5,需要将col3 value完全"走过",当col3 value由于过大而分散存储在多个溢出页时,还需要"走过"所有这些溢出页,虽然这些"走过"完全是无意义的,但仍然会发生IO。
由于SQLite的文件格式有上述特征,因此只需将BLOB字段顺序由第3位调整为最末位,即可避免对BLOB字段无效的IO"走过"。
仍然使用4.2中的数据,设PAGE_SIZE为1K,将BLOB字段分别设为中间位置和最末位创建数据库,比较性能如下:
|
PAGE_SIZE |
BLOB字段的位置 |
该使用场景查询耗时 |
|
1K |
中间位置 |
4.93s |
|
1K |
最末位 |
0.28s |
将BLOB字段由中间位置调整为最未位之后,优化效果明显,查询效率约为调整前的17.6倍。
5.结论
关于第一章背景中提到的优化场景,有如下两种优化手段:
- 将BLOB字段由中间位置调整为最末位。此种优化手段优化效果非常明显。
- 根据表记录的大小,设置合适的PAGE_SIZE,以尽量减少溢出页,进而减少IO次数。此种优化方式优化效果尚可,但没有第一种优化手段效果明显。
SQLite中字段顺序和PAGE_SIZE对性能的影响的更多相关文章
- 关于数据库优化1——关于count(1),count(*),和count(列名)的区别,和关于表中字段顺序的问题
1.关于count(1),count(*),和count(列名)的区别 相信大家总是在工作中,或者是学习中对于count()的到底怎么用更快.一直有很大的疑问,有的人说count(*)更快,也有的人说 ...
- 一天五道Java面试题----第九天(简述MySQL中索引类型对数据库的性能的影响--------->缓存雪崩、缓存穿透、缓存击穿)
这里是参考B站上的大佬做的面试题笔记.大家也可以去看视频讲解!!! 文章目录 1.简述MySQL中索引类型对数据库的性能的影响 2.RDB和AOF机制 3.Redis的过期键的删除策略 4.Redis ...
- Android中查看SQLite中字段数据的两种方式
方式一:ADB Pull 通过adb pull导出*.db文件到PC的文件夹中,通过可视化工具 SQLiteExpertPers 进行查看.编辑: adb pull /data/data/com.jo ...
- SQL where 条件顺序对性能的影响有哪些
经常有人问到oracle中的Where子句的条件书写顺序是否对SQL性能有影响,我的直觉是没有影响,因为如果这个顺序有影响,Oracle应该早就能够做到自动优化,但一直没有关于这方面的确凿证据.在网上 ...
- 优化 : Oracle数据库Where条件执行顺序 及Where子句的条件顺序对性能的影响
.Oracle数据库Where条件执行顺序: 由于SQL优化起来比较复杂,并且还会受环境限制,在开发过程中,写SQL必须必须要遵循以下几点的原则: 1.ORACLE采用自下而上的顺序解析WHERE子句 ...
- SQL Server中多表连接时驱动顺序对性能的影响
本文出处:http://www.cnblogs.com/wy123/p/7106861.html (保留出处并非什么原创作品权利,本人拙作还远远达不到,仅仅是为了链接到原文,因为后续对可能存在的一些错 ...
- 在sqlite中,如何删除字段? how to drop a column in sqlite
在sqlite中可以使用ALTER TABLE语法对表结构进行修改,从官方的文档说明中,语法如下图: 从图中可以看出,ALTER TABLE仅仅支持表名重命名,添加字段,却没有删除字段的方法.那么该如 ...
- 解决SpringDataJpa实体类中属性顺序与数据库中生成字段顺序不一致的问题
一.在application.yml配置中添加数据库根据实体类自动创建数据库表的配置(这里数据库采用MySQL数据库) jpa: database: MYSQL show-sql: true #Hib ...
- 在oracle表中增加、修改、删除字段,表的重命名,字段顺序调整
增加字段语法:alter table tablename add (column datatype [default value][null/not null],….); 说明:alter table ...
随机推荐
- SVN客户端安装
1.安装客户端 双击运行: 点击[next] 点击[next] 选择好路径后,点击[next] 点击[install]安装 点击[finish]完成安装.安装完成后重启计算机. 2.客户端访问SVN服 ...
- 解决plsql中文显示问号(???)问题
最近新买的电脑,配置好数据库连接后,plsql查看数据与插入中文数据都显示问号(???),同事的都正常显示,查看了很多资料,有的说是数据库字符集的原因让修改数据库的字符集,但是我的数据库都是远程连接正 ...
- python--线程和进程的初识
一.进程与线程之间的关系 1.线程是属于进程的,线程运行在进程空间内,同一进程所产生的线程共享同一内存空间,当进程退出时该进程所产生的线程都会被强制退出并清除. 2.线程可与属于同一进程的其它线程共享 ...
- 微服务架构 ------ 插曲 Linux平台 Ubuntu的安装
1.一定要通过自定义安装 2.选择的硬件兼容性选择 14.x 这里介绍一下红框内的东西,是为了做虚拟存储使用的,也就是一批服务器对外展示位一个服务器,类似于服务器集群 3.选择稍后安装操作系统,如 ...
- Invalid attempt to spread non-iterable instance
问题在于对数据的操作,或数据类型,或数据名称
- make CMake 来龙去脉
理论上说,任意一个C++程序都可以用g++来编译. 大家都知道,写程序大体步骤如下: 1.用编辑器编写源代码,如.c文件. 2.用编译器编译代码生成目标文件,如.o. 3.用链接器连接目标代码生成可执 ...
- JavaWeb 发送邮件
我们可以使用第三方的邮箱服务器来发送邮件. 常用的邮件传输协议有2种:POP3/SMTP.IMAP/SMTP. POP和IMAP的区别:在邮箱客户端的操作,比如移动邮件.标记已读,如果使用POP,是不 ...
- FPM八:FPM TREE
先上效果图: 1,新建类:ZCL_FPM_TREE,并添加接口:IF_FPM_GUIBB,IF_FPM_GUIBB_TREE.激活所有方法 2.定义tree结构,在class的public secti ...
- 类型转换 String——》Char OR Char ——》String
String转换为char 在Java中将String转换为char是非常简单的.1. 使用String.charAt(index)(返回值为char)可以得到String中某一指定位置的char.2 ...
- elastalert docker安装
基于对elasticsearch中数据监控需要,我尝试了sentinl和elastalert两款工具.虽然elastalert是纯文本,但易配置管理.elk自带的watch需要付费才可使用. 6.2x ...