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优化,一般来说,优化无外乎如下几种常用的方式:

  1. 索引。但对于需要进行遍历访问的表,通过索引显然毫无优化空间,甚至会效率更低。因此索引的优化思路首先被放弃。
  2. SQL优化。但这个SQL,属于最基本的查询SQL,因此也没有优化空间。
  3. 并行查询。将之前1个进程访问所有的记录,改为多个进程分别访问不同的记录区间。这种方式可以尝试。
  4. 参数优化。例如通过设置PAGE_SIZE参数,调整最小存储单元PAGE的大小。
  5. 其它优化。基于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.结论

关于第一章背景中提到的优化场景,有如下两种优化手段:

  1. 将BLOB字段由中间位置调整为最末位。此种优化手段优化效果非常明显。
  2. 根据表记录的大小,设置合适的PAGE_SIZE,以尽量减少溢出页,进而减少IO次数。此种优化方式优化效果尚可,但没有第一种优化手段效果明显。

SQLite中字段顺序和PAGE_SIZE对性能的影响的更多相关文章

  1. 关于数据库优化1——关于count(1),count(*),和count(列名)的区别,和关于表中字段顺序的问题

    1.关于count(1),count(*),和count(列名)的区别 相信大家总是在工作中,或者是学习中对于count()的到底怎么用更快.一直有很大的疑问,有的人说count(*)更快,也有的人说 ...

  2. 一天五道Java面试题----第九天(简述MySQL中索引类型对数据库的性能的影响--------->缓存雪崩、缓存穿透、缓存击穿)

    这里是参考B站上的大佬做的面试题笔记.大家也可以去看视频讲解!!! 文章目录 1.简述MySQL中索引类型对数据库的性能的影响 2.RDB和AOF机制 3.Redis的过期键的删除策略 4.Redis ...

  3. Android中查看SQLite中字段数据的两种方式

    方式一:ADB Pull 通过adb pull导出*.db文件到PC的文件夹中,通过可视化工具 SQLiteExpertPers 进行查看.编辑: adb pull /data/data/com.jo ...

  4. SQL where 条件顺序对性能的影响有哪些

    经常有人问到oracle中的Where子句的条件书写顺序是否对SQL性能有影响,我的直觉是没有影响,因为如果这个顺序有影响,Oracle应该早就能够做到自动优化,但一直没有关于这方面的确凿证据.在网上 ...

  5. 优化 : Oracle数据库Where条件执行顺序 及Where子句的条件顺序对性能的影响

    .Oracle数据库Where条件执行顺序: 由于SQL优化起来比较复杂,并且还会受环境限制,在开发过程中,写SQL必须必须要遵循以下几点的原则: 1.ORACLE采用自下而上的顺序解析WHERE子句 ...

  6. SQL Server中多表连接时驱动顺序对性能的影响

    本文出处:http://www.cnblogs.com/wy123/p/7106861.html (保留出处并非什么原创作品权利,本人拙作还远远达不到,仅仅是为了链接到原文,因为后续对可能存在的一些错 ...

  7. 在sqlite中,如何删除字段? how to drop a column in sqlite

    在sqlite中可以使用ALTER TABLE语法对表结构进行修改,从官方的文档说明中,语法如下图: 从图中可以看出,ALTER TABLE仅仅支持表名重命名,添加字段,却没有删除字段的方法.那么该如 ...

  8. 解决SpringDataJpa实体类中属性顺序与数据库中生成字段顺序不一致的问题

    一.在application.yml配置中添加数据库根据实体类自动创建数据库表的配置(这里数据库采用MySQL数据库) jpa: database: MYSQL show-sql: true #Hib ...

  9. 在oracle表中增加、修改、删除字段,表的重命名,字段顺序调整

    增加字段语法:alter table tablename add (column datatype [default value][null/not null],….); 说明:alter table ...

随机推荐

  1. 史上最全的音视频SDK包分享给大家

    史上最全的音视频SDK包分享给大家 概述一下SDK功能: 项目 详情视频通信  支持多种分辨率的视频通信语音通信  提供语音通信,可支持高清宽带语音动态创建房间  可以根据需要,随时创建房间H5 支持 ...

  2. mvc返回json数据

    不知道是不是你要的那种,这是用于返回简单数据表达数据操作成功,中间复杂代码掠过看首尾 [HttpPost] public IActionResult DataRole(int id, string[] ...

  3. Sqlserver表值函数来获取逗号分隔的ID

    其功能为: 将字符串如'1,2,3,4,5,6' 拼接成SQL里面的id 1:使用: select * from Student where id IN( SELECT * FROM dbo.F_SP ...

  4. docker基础应用

    环境: centos 7  docker 19.03.0-beta3  192.168.10.10 关于如何安装docker请参考:https://www.cnblogs.com/caesar-id/ ...

  5. Flask模板渲染

    目录 Flask模板渲染 Jinja2模板引擎简介 模板 Jinja2 模板变量 变量 控制结构 宏,类似Python代码中的函数 模板继承 包含(Include) 过滤器 链式调用 常见内建过滤器 ...

  6. mysql的my.cnf

    配置参数详解 [client] #客户端设置,即客户端默认的连接参数port = 3307   #默认连接端口socket = /data/mysqldata/3307/mysql.sock #用于本 ...

  7. sql的日期格式化转化

    1. DATE_FORMAT() 函数用于以不同的格式显示日期/时间数据. DATE_FORMAT(date,format) 可以使用的格式有: 格式 描述 %a 缩写星期名 %b 缩写月名 %c 月 ...

  8. 分布式CAP定理

    根据百度百科的定义,CAP定理又称CAP原则,指的是在一个分布式系统中,Consistency(一致性). Availability(可用性).Partition tolerance(分区容错性),最 ...

  9. 让天堂的归天堂,让尘土的归尘土——谈Linux的总线、设备、驱动模型

    本文系转载,著作权归作者所有.商业转载请联系作者获得授权,非商业转载请注明出处. 作者: 宋宝华 来源: 微信公众号linux阅码场(id: linuxdev) 公元1951年5月15日的国会听证上, ...

  10. Fiddler抓包设置

    介绍 Fiddler 在 PC 端和移动端,模拟器抓取数据包 Fiddler抓取PC端数据包: 这里 Fiddler 抓取网页客户端的数据包时,其原理就是在 客户端/浏览器 和 服务器端 之间,加上了 ...