Android SQLite性能分析————c博客精品推荐
2015-07-15 10:17
155 查看
作为Android预置的数据库模块,对SQLite的深入理解是非常有必要的,可以从中找到一些优化的方向。这里对SQLite的性能和内存进行了一些测试分析,对比了不同操作的执行性能和内存占用的情况,粗略地列在这里算是作个小结。
先了解一下SQLite主要架构 (详见《The Definitive Guide to SQLite》), 需要关注的是Compiler和Backend两个模块。正因为有一个虚拟机的存在,所以才有了Compiled Statement的价值,因为它可以减少前置的编译时间,直接到VDBE上执行。而Backend端的Pager,则是重要数据管理者,真正决定者数据操作的性能,以及内存占用。
![](http://img.blog.csdn.net/20140429220556000?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvSG9ya3lDaGVu/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
这里不再赘述,详细的内容还是阅读有关SQLite介绍的资料。
这次测试基于Sumsung i9103和Google Nexus S1进行。使用Python脚本及adb指令通过Intent操作Android应用进行数据库操作。
首先观察到SELECT在不同记录数下的峰值分布,可见整体上有上升的趋势。正如SQLite官方说的它并不适合存储大量数据,一定要控制其中的记录数量。
另外SELECT操作性能也取决于查询的栏位个数,而@行云进一步确认和返回栏位的内容大小有关,也就是栏位的内容和多少也需要权衡。
![](http://img.blog.csdn.net/20140429220805343?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvSG9ya3lDaGVu/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
事务是提高SQLite操作性能的一个重要技术,特别是SQLite实现了WAL,使得事务与SELECT不会互斥,大大提高了应用的性能。参见附2。
下面是未使用事务时,三类操作的平均值:
![](http://img.blog.csdn.net/20140429220857390?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvSG9ya3lDaGVu/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
使用了事务后,各个操作的性能大幅下降。
![](http://img.blog.csdn.net/20140429220920984?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvSG9ya3lDaGVu/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
但是问题是事务提交的时间会变长,这个时间一是需要形成新的峰值,另外也要平分到各个操作来看:
![](http://img.blog.csdn.net/20140429220937750?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvSG9ya3lDaGVu/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
所以不能贪多,事务还是要及时提交。另外注意, 虽然Compiled Statement有利于性能表现,但还不如使用事务的效果来得直接。
先看SELECT操作的平均值在两种机型上的表现, 可以看到i9103上的波动性比较大,而Nexsus S1则一直保持稳定:
![](http://img.blog.csdn.net/20140429221041265?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvSG9ya3lDaGVu/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
散点图,可以观察到时间分布情况:
![](http://img.blog.csdn.net/20140429221138046?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvSG9ya3lDaGVu/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
再看另外三个操作的表现(上下两部分false和true分别表示为未使用事务和使用事务的情况):
![](http://img.blog.csdn.net/20140429221153984?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvSG9ya3lDaGVu/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
运用WAL可以大幅提升性能,不过它也有一个副作用。它会导致数据查询需要进行两次,可以理解为一次在db文件,一次在wal文件。具体原因参见<<The Definitive Guide to SQLite>>最后一章。WAL是满1000Pages时才会合并到主数据库里,这个时机称为checking point, 可以进行配置。按默认的Page
Size:1024计算,也就是当WAL文件接近1M时,进行检查。如果没有活动的事务使用这些pages,就会提交。而WAL的大小,对SELECT的影响表现不同。
下面是WAL对查询性能峰值影响的测试数据(i9103上测试),图中的true,false表示是否有WAL文件存在。
![](http://img.blog.csdn.net/20140429221221109?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvSG9ya3lDaGVu/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
*整体的平均值相差在1ms,但峰值的才是真正值得注意的。
为了避免WAL过大,可以选择调用SQLiteDatabase::disableWriteAheadLogging()强制合并到主数据库文件,这个调用会异步在SQLite内部执行,不会明显阻塞用户的线程。
*另外,SQLiteDatabase::query()只是对SQLiteDatabase::rawQuery()的封装,多了一个字串组装的过程,反而不如直接使用SQLiteDatabase::rawQuery()。
SQLite以Page为单位存储数据,默认一个Page有1024字节,然后通过B- Tree组织起来(Table使用B+ Tree组织):
![](http://img.blog.csdn.net/20140429221254109?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvSG9ya3lDaGVu/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
Lookaside则是SQLite应用的内存管理的技术,优化了内存的使用效率,主要思想是先分配一整块内存, 分成若干个slots,然后SQLite再按需使用。这和许多小内存分配器的思想是一样的。详见附1。
再解释一下Page Cache Overflow, 主要是在一个Page中的记录的数据无法刚好放在一个Page内,还要使用额外的另一个Page空间, 这就是Overflow Page。
![](http://img.blog.csdn.net/20140429221317296?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvSG9ya3lDaGVu/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
Android还有个万能dumpsys, 使用dumpsys meminfo可以查看到一个进程的SQLite使用的内存信息。如:
SQL
heap: 265 MEMORY_USED: 265
PAGECACHE_OVERFLOW: 73 MALLOC_SIZE: 46
DATABASES
pgsz dbsz Lookaside(b) cache Dbname
4 60 17 199/114/1 webviewCache.db
1/541/1 (pooled # 1) webviewCache.db
. cache的三个值分别是:
Page Cache的命中次数、未命中次数,以及Page Cache个数。可以在Android源码中的SQLiteDebug.java以及ActivityThread.java找到细节的内容。
. page size, db size的单位是KBytes, Lookaside(b)是指使用多少个Lookaside的slots。
. 对于Heap和Overflow Pages,SQLiteDatabase的内存回收可能没有那么及时,可以调用SQLiteDatabase::releaseMemory()进行主动释放。
*使用SQLite的PRAGMA可以直接获取一些通过SQLiteDatabase获取不到信息,当然如果SQLite不支持,也会抛异常出来,详见附4。
转载请注明出处: http://blog.csdn.net/horkychen SQLite是一个非常精致的系统,很值得研究学习。
1. SQLite Dynamic Memory
Allocation
2. Write-Ahead Logging 或 SQLite的WAL机制
3. The Definitive Guide to SQLite( SQLite权威指南, 两处关于架构和Overflow page的截图来于此书.)
4. PRAGMA
Statement
5. 官方文档
6. NEC关于Android系统上存储器操作性能的研究报告 (里面有讲述SQLite因为其随机性访问的机制而在不同文件系统上的性能差异)
7. Android开发中的SQLite优化
1. 基本架构
先了解一下SQLite主要架构 (详见《The Definitive Guide to SQLite》), 需要关注的是Compiler和Backend两个模块。正因为有一个虚拟机的存在,所以才有了Compiled Statement的价值,因为它可以减少前置的编译时间,直接到VDBE上执行。而Backend端的Pager,则是重要数据管理者,真正决定者数据操作的性能,以及内存占用。这里不再赘述,详细的内容还是阅读有关SQLite介绍的资料。
2. 性能
这次测试基于Sumsung i9103和Google Nexus S1进行。使用Python脚本及adb指令通过Intent操作Android应用进行数据库操作。
i. SELECT操作
首先观察到SELECT在不同记录数下的峰值分布,可见整体上有上升的趋势。正如SQLite官方说的它并不适合存储大量数据,一定要控制其中的记录数量。另外SELECT操作性能也取决于查询的栏位个数,而@行云进一步确认和返回栏位的内容大小有关,也就是栏位的内容和多少也需要权衡。
ii. 关于事务
事务是提高SQLite操作性能的一个重要技术,特别是SQLite实现了WAL,使得事务与SELECT不会互斥,大大提高了应用的性能。参见附2。下面是未使用事务时,三类操作的平均值:
使用了事务后,各个操作的性能大幅下降。
但是问题是事务提交的时间会变长,这个时间一是需要形成新的峰值,另外也要平分到各个操作来看:
所以不能贪多,事务还是要及时提交。另外注意, 虽然Compiled Statement有利于性能表现,但还不如使用事务的效果来得直接。
iii. 不同机型的操作性能
先看SELECT操作的平均值在两种机型上的表现, 可以看到i9103上的波动性比较大,而Nexsus S1则一直保持稳定:散点图,可以观察到时间分布情况:
再看另外三个操作的表现(上下两部分false和true分别表示为未使用事务和使用事务的情况):
运用WAL可以大幅提升性能,不过它也有一个副作用。它会导致数据查询需要进行两次,可以理解为一次在db文件,一次在wal文件。具体原因参见<<The Definitive Guide to SQLite>>最后一章。WAL是满1000Pages时才会合并到主数据库里,这个时机称为checking point, 可以进行配置。按默认的Page
Size:1024计算,也就是当WAL文件接近1M时,进行检查。如果没有活动的事务使用这些pages,就会提交。而WAL的大小,对SELECT的影响表现不同。
下面是WAL对查询性能峰值影响的测试数据(i9103上测试),图中的true,false表示是否有WAL文件存在。
*整体的平均值相差在1ms,但峰值的才是真正值得注意的。
为了避免WAL过大,可以选择调用SQLiteDatabase::disableWriteAheadLogging()强制合并到主数据库文件,这个调用会异步在SQLite内部执行,不会明显阻塞用户的线程。
*另外,SQLiteDatabase::query()只是对SQLiteDatabase::rawQuery()的封装,多了一个字串组装的过程,反而不如直接使用SQLiteDatabase::rawQuery()。
3. 内存
SQLite以Page为单位存储数据,默认一个Page有1024字节,然后通过B- Tree组织起来(Table使用B+ Tree组织):Lookaside则是SQLite应用的内存管理的技术,优化了内存的使用效率,主要思想是先分配一整块内存, 分成若干个slots,然后SQLite再按需使用。这和许多小内存分配器的思想是一样的。详见附1。
再解释一下Page Cache Overflow, 主要是在一个Page中的记录的数据无法刚好放在一个Page内,还要使用额外的另一个Page空间, 这就是Overflow Page。
Android还有个万能dumpsys, 使用dumpsys meminfo可以查看到一个进程的SQLite使用的内存信息。如:
SQL
heap: 265 MEMORY_USED: 265
PAGECACHE_OVERFLOW: 73 MALLOC_SIZE: 46
DATABASES
pgsz dbsz Lookaside(b) cache Dbname
4 60 17 199/114/1 webviewCache.db
1/541/1 (pooled # 1) webviewCache.db
. cache的三个值分别是:
Page Cache的命中次数、未命中次数,以及Page Cache个数。可以在Android源码中的SQLiteDebug.java以及ActivityThread.java找到细节的内容。
. page size, db size的单位是KBytes, Lookaside(b)是指使用多少个Lookaside的slots。
. 对于Heap和Overflow Pages,SQLiteDatabase的内存回收可能没有那么及时,可以调用SQLiteDatabase::releaseMemory()进行主动释放。
*使用SQLite的PRAGMA可以直接获取一些通过SQLiteDatabase获取不到信息,当然如果SQLite不支持,也会抛异常出来,详见附4。
转载请注明出处: http://blog.csdn.net/horkychen SQLite是一个非常精致的系统,很值得研究学习。
参考
1. SQLite Dynamic MemoryAllocation
2. Write-Ahead Logging 或 SQLite的WAL机制
3. The Definitive Guide to SQLite( SQLite权威指南, 两处关于架构和Overflow page的截图来于此书.)
4. PRAGMA
Statement
5. 官方文档
6. NEC关于Android系统上存储器操作性能的研究报告 (里面有讲述SQLite因为其随机性访问的机制而在不同文件系统上的性能差异)
7. Android开发中的SQLite优化
相关文章推荐
- Android:通过SpannableString为TextView设置丰富的显示效果
- Android安全退出应用程序的几种方式ying用
- android5.0编译问题:No rule to make target 'external/chromium_org/third_party/angle/.git/index',
- Listview的onItemClickListener无法响应的问题
- android中include标签的使用
- Android中将Bitmap对象以PNG格式保存在内部存储中
- 9个常见的Android开发错误及解决办法
- Android 顶部灰条标题栏不显示的方法
- pulltorefresh自动刷新
- Android中RelativeLayout各个属性的含义
- Android电子书项目实训【项目说明】【1】
- Android入门(52)——第七章 Android中常用的系统服务
- Android 使用NFC通过标签协议栈读写标签数据
- Android获取文件目录
- android 更新实现自己主动
- android 彩信附件添加删除
- Android 打印文件名、函数名、行号
- Error: MediaPlayer start called in state 1 & 64
- 还使用SVN1.6版本的Android开发者注意了。ADT与SVN的兼容性错误。ADT更新的高发性问题
- 使用Android Studio新建Project并建立多个module