高频访问SQLite数据库
SQLite 是一款开源的 SQL 数据库引擎,由于其自包含、无服务、零配置和友好的使用许可(完全免费)等特点,在桌面和移动平台被广泛使用。
在应用开发过程中,如果想保存点数据,自然而然地就会想到 SQLite,毕竟它拥有非常多的实践者。这里分享一个在项目开发过程中遇到的 SQLite 读写问题——在开发一个小型桌面应用系统时,需求是跟踪文件系统中的变更,同时对变更文件进行相关操作,我们毫不犹豫地采用了 SQLite 来存储文件变更信息。
在开发过程中,SQLite 的数据读写都非常顺利,没有什么障碍。然而,当业务逻辑一切就绪开始跑业务时,我们发现软件处理业务的性能很差,每秒钟只能处理 10 个左右的业务量,比数据放在内存的老系统还慢得多。老系统也还可以达到每秒三十几个业务,而现在只有三分之一的水平。在有几千几万个文件变更事件同时涌入的情况下,系统几近停滞,会出现几秒钟一个业务的荒凉场景。这是不能容忍的事情。
经过技术排查,我们发现对 SQLite 的读和写都非常慢,最差的情况是从数据库中获取一条记录要花掉 7 秒钟,十分离谱。于是我们收罗学习了各种 SQLite 的优化技术并应用到了系统之中:
- SQL 操作时采用事务机制
sqlite3_exec(db,"BEGIN TRANSACTION;",0,0,0);
...
sqlite3_exec(db,"END TRANSACTION;",0,0,0);
- 批量操作时,使用sqlite3_prepare而不是sqlite3_exec
sqlite3_prepare_v2(db, zSQL, -1,&stmt, &pzTail);
sqlite3_step(stmt);
...
- 关闭数据库的磁盘同步写,降低数据安全性
sqlite3_exec(db,"PRAGMA synchronous = OFF; ",0,0,0);
常见的优化技术都已使用,效果有但不太理想,还是没有达到老系统的性能,更不要说超过了。
这里需要回顾一下我们的应用模型。业务有并发处理的要求,系统中使用了多线程机制,这就出现了对 SQLite 并发多读多写的情况。我们查阅 SQLite 的官方文档,多写者的情况是不适用的。
至此,是不是说解决的出路就只有使用 client/server 这样的数据库了?小应用拖一个巨无霸数据库,有种头重脚轻的感觉。
记得数据库课程的学习中,有提到大型数据库访问的 多层模型(N-tier),目的就是更高效地处理数据。那我们的文件型数据库有没有可能拥有 N-tier 的思想?尽管与大型数据库的方法不一样,但目的是一致的。我们分析一下现有应用对 SQLite 的读写情况,先看图:

操作1
收到文件系统中的变更信息,并写入到数据库。由于文件变更信息是逐条发生的,无法预估事件的开始和结束,来一条写一条的方式,导致开启SQLite的事务模式也没有啥效果。操作2
读取一条记录并进行业务操作,这里的读取并非只读,需要将该条记录标记为已选取,防止被其他业务处理线程读取而引发重复处理。因此,这一步也存在写操作。这里是读一条处理一条。操作3
业务处理完毕后,从数据库中删除。这里也是逐条删除。
回顾应用的业务操作方式后发现,这些操作都是写操作,而且还是逐条进行的。问题摆在这里,技术问题还是需要通过技术来解决。在优化的过程中,我们是分步骤进行的——
优化操作1
采用延迟写的机制,收到文件变更信息后,不立即写入数据库,先放入缓存队列,等到达一定时间后再进行批量写入,这样在大量事件涌入时效果明显,大大减少了数据库的写操作次数。优化操作2
使用缓存;好不容易准备好数据库查询语句,只检索了一条,太浪费时机,将符合检索要求的记录缓存起来。同时将记录被选取的标记放在内存中而不写数据库,这样对数据库来说仅是读操作。优化操作3
同样采用延迟写,将收到的删除信息缓存起来,当累积到一定量或者时间后,再进行批量操作。这样就可以充分利用 SQLite 的事务功能,大大提升写操作的效率。
增加了这些数据库访问层后,数据库的读写性能提升明显,业务处理能力也达到了预期,超过了旧系统,主要的优化工作差不多就到此结束了。这里引入了延迟写和缓存机制,增加了程序的复杂度,带来的新挑战是如何保持缓存记录同数据库记录的一致性。为解决这个问题,使用了SQLite的自定义函数:
sqlite3_create_function(...);
通过创建自定义函数,来同步缓存记录和数据库记录。比如:在从数据库读取业务记录时,需要排除已经被标为"删除"的记录。
经历这个项目,我们让 SQLite 多读多写的并发访问也成为了可能,算是一个收获。(徐品华 | 天存信息)
高频访问SQLite数据库的更多相关文章
- Qt5 开发 iOS 应用之访问 SQLite 数据库
开发环境: macOS 10.12.1 Xcode 8.1 Qt 5.8 iPhone 6S+iOS 10.1.1 源代码: 我在 Qt 程序里指定了数据库的名称来创建数据库,在 Win10.An ...
- 【Android】13.4 使用SQLite.NET.Async-PCL访问SQLite数据库
分类:C#.Android.VS2015: 创建日期:2016-02-27 一.简介 这一节演示如何利用以异步方式(async.await)访问SQLite数据库. 二.示例4运行截图 下面左图为初始 ...
- 【Android】13.2 使用自定义的CursorAdapter访问SQLite数据库
分类:C#.Android.VS2015: 创建日期:2016-02-26 一.简介 SQliteDemo1的例子演示了SimpleCursorAdapter的用法,本节我们将使用用途更广的自定义的游 ...
- 【Android】13.1 用Android自带的API访问SQLite数据库
分类:C#.Android.VS2015: 创建日期:2016-02-26 一.简介 这一节我们先来看看如何直接用Android自带的API创建和访问SQLite数据库. 1.创建SQLite数据库 ...
- 【Android】13.0 第13章 创建和访问SQLite数据库—本章示例主界面
分类:C#.Android.VS2015: 创建日期:2016-02-26 一.简介 Android 内置了三种数据存取方式:SQLite数据库.文件.SharedPreferences. 这一章我们 ...
- 并发访问sqlite数据库出现databse is locked的错误的一个解决办法
作者:朱金灿 来源:http://blog.csdn.net/clever101 在并发访问sqlite数据库会出现这样一个错误:databseis locked,这是sqlite数据库对并发支持不太 ...
- 【C#】使用EF访问Sqlite数据库
原文:[C#]使用EF访问Sqlite数据库 1. 先上Nuget下载对应的包 如图,搜索System.Data.SQLite下载安装即可,下载完之后带上依赖一共有这么几个: EntityFramew ...
- 以EntifyFramework DBFirst方式访问SQLite数据库
前面一直在找EF Code First方式来访问SQLite数据库,后面得出的结论是SQLite不支持 Code First, 虽然有非官方的库SQLite.CodeFirst可以使用,但一直没搞成功 ...
- 【Android】13.3 使用SQLite.NET-PCL访问SQLite数据库
分类:C#.Android.VS2015: 创建日期:2016-02-26 一.简介 本章开头已经说过了,SQLite.NET-PCL用起来很爽,这一节咱们看看怎样使用吧. 二.示例3运行截图 下面左 ...
随机推荐
- 05- 移动端APP的分类 与对比webApp hybridApp nativeApp
随着智能手机的普及,移动端应用几乎成为每个互联网产品的标配.在快速迭代的互联网战场中高效开发.低成本上线产品,是每个应用开发团队追求的目标.此时,选择合适的应用类型和开发模式便至关重要.移动应用可以粗 ...
- 软件篇-01-为Jetson TX2扫清科研的障碍
建议用vryL,GUI界面,功能更全,支持订阅. https://github.com/ShieldQiQi/vryLgithub.com 下面的内容不用看了,除非你更喜欢命令行界面. To r ...
- Windows系统之间文件互传
1)利用Windows自带的文件共享服务 本次试验以Win7为服务器端,win10为客户端 1.确保Win7服务端开启对应的服务及开放相应的端口号 进入命令行界面,输入netstat -an,查看44 ...
- Windows 驱动加载程序代码
#include <windows.h> #include <winsvc.h> #include <conio.h> #include <stdio.h&g ...
- 修改linux默认文件创建权限-umask命令解析
umask值用于设置用户在创建文件时的默认权限,当我们在系统中创建目录或文件时,目录或文件所具有的默认权限就是由umask值决定的. 对于root用户,系统默认的umask值是0022:对于普通用户, ...
- 【SpringMVC】数据校验时,抛出javax.validation.UnexpectedTypeException: HV000030: No validator could be found for type: java.util.Date.
老魏原创,转载请留言. 原因:给Javabean中的字段注解多余或者错误导致. @NotEmpty @Past @DateTimeFormat(pattern="yyyy-MM-dd&quo ...
- 简单说几个MySQL高频面试题
前言: 在各类技术岗位面试中,似乎 MySQL 相关问题经常被问到.无论你面试开发岗位或运维岗位,总会问几道数据库问题.经常有小伙伴私信我,询问如何应对 MySQL 面试题.其实很多面试题都是大同小异 ...
- MySQL修改账号密码方法大全
前言: 在日常使用数据库的过程中,难免会遇到需要修改账号密码的情景,比如密码太简单需要修改.密码过期需要修改.忘记密码需要修改等.本篇文章将会介绍需要修改密码的场景及修改密码的几种方式. 1.忘记 r ...
- DVWA--SQL Injection
sql注入是危害比较大的一种漏洞,登录数据库可以进行文件上传,敏感信息获取等等. Low 先来看一下源码 <?php if( isset( $_REQUEST[ 'Submit' ] ) ) { ...
- .Net平台的GC垃圾回收
一.先了解下必备的知识前提 内存中的托管与非托管,可简单理解为: 托管:可借助GC从内存中释放的数据对象(以下要描述的内容点) 非托管:必须手工借助Dispose释放资源(实现自IDisposable ...