zoukankan      html  css  js  c++  java
  • Android SQLite性能分析

    作为Android预置的数据库模块,对SQLite的深入理解是很有必要的,能够从中找到一些优化的方向。

    这里对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 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优化





  • 相关阅读:
    Leetcode86.分隔链表
    Leetcode39.组合总和
    Leetcode31.下一个排列
    剑指Offer35.复杂链表复制
    剑指Offer14-I.剪绳子
    剑指Offer38.字符串的排序
    Leetcode29.两数相除
    232. Implement Queue using Stacks
    程序员跳槽指南
    226. Invert Binary Tree
  • 原文地址:https://www.cnblogs.com/claireyuancy/p/6798538.html
Copyright © 2011-2022 走看看