sqliteAndroid SQLite总结

  SQLite在Android一般采用被尚是于常用,早期的时刻遇到过众多坑,其中最为烦的尽管是多线程并发读写题目,今天刚好整理一下,做个记,也接指正、讨论以及补偿。

  一、查询优化

  1、wal模式

  开启wal模式,可以兑现并发读,且读写不死,当然写及写之间仍阻塞,该模式需android3.0+才支撑。

  当打开了wal模式创新数据常常,会先将数据写入到*.db-wal文件被,而非是直改动数据库文件,当执行checkpoint时或者某个时刻接触才会用数据更新到数据库文件(执行endTransaction时会交checkpoint)。当起rollback也止是解wal日志文件,而ROLLBACK
JOURNAL模式,也不怕是关闭wal模式时,当数有更新时,先将需改的数据备份到journal文件被,然后修改数据库文件,当有rollback,从journal日志中取出数据,并修改数据库文件,然后去掉journal日志。
从上述流程来看wal在数量更新上I/O量要稍微,所以写操作而赶早。由于当读取数据时为要读取wal日志验证数据的不错,所以读取数据相对要慢,但使用wal还是增强了读取的并发性。

  开启wal模式继,一定要动beginTransactionNonExclusive来交付业务。db.beginTransaction()相当于execSQL(“BEGIN
EXCLUSIVE;”),在时下政工没有终结前任何其他线程或进程都无法对数据库进行读写操作。当被wal模式时,使用db.beginTransactionNonExclusive(),相当于execSQL(“BEGIN
IMMEDIATE;”),只会克其他线程对数据库的描写操作,不见面阻塞读操作。

  2、建立目录,推荐看之稿子,足够了解索引的概括以和优点了http://www.trinea.cn/android/database-performance/,总而言之,索引会增加SQLite体积,且增删改时也要维护索引,会针对增删改性在必然影响,如果数据量不老,不建议利用。使用时肯定要是因需要建立适宜的目,勿滥用。

  3、当某张表可预见数据量很充分时,可以方便的进展说明底细化、后期可分表分库,查询时为足以以异步查询。

 

  二、批量栽优化

  1、事务提交

  批量插,包括创新删除,一定要加事务,如果非加事务,则默认会为每一样次于栽开启一个事务并自行提交,是蛮慢的。

  2、开启wal模式,参见上文中说;

  3、SQLiteStatement优化

  我们每次执行之sql语句最终会转化为一个SQLiteStatement对象来进行处理,可以先使用db.compileStatement方法赢得SQLiteStatement对象并引用,而无是吃系统每次insert都组织一个应和的SQLiteStatement对象,这样会提高内存的使用率。

  补充:网上有人说“比如insert into
xxx,一般情况下执行多少次,就要编译多少次
”,关于这点,首先自己当畸形,我论一下本人自己的分析:SQLite想如果实施操作,需要用顺序中之sql语句进行“预编译”。例如批量安插,我们得采用“显式预编译”来好用SQLiteStatement,也即是以compileStatement方法。其实根本在SQLiteStatement对象在new时,会通过SQLiteSession获取连接池中有SQLiteConnection,通过调用SQLiteConnection的prepare方法,会由PreparedStatement链表中获,如果没但选用的虽会创造一个PreparedStatement对象,其中会做一些native操作,例如给PreparedStatement的mStatementPtr赋值,通过注释,我们得以了解及这mStatementPtr就是一个对sqlite3_stmt类型的指针,而sqlite3_stmt是sqlite自己之中的数据结构,用来记录“sql语句”,这个sql语句是分析后底,也就是“预编译”后的。

  一词话,就是new
SQLiteStatement会对sql做预编译,如果就预编译过,会直接从缓存链表中以。

  其实从点分析,我们明白每个SQLiteConnection都含有一个链表结构的PreparedStatemnt对象集合,每次得到SQLiteConnection都见面先找到包含sql预编译的PreparedStatement实例的数据库连接,这样便无见面每次都去预编译sql。所以只有此connection刚好被其他线程拿去用了,否则都得到相同的connection,不用再行预编译。也就是说在已施行了千篇一律软预编译(生成PreparedStatement实例)的SQLiteConnection中,不会见重复累预编译,即使你inser
into
n次,而招致你用重预编译sql的图景是SQLiteConnection恰巧被其他线程使用,就会又acquirePreparedStatement。

private PreparedStatement acquirePreparedStatement(String sql) {
    PreparedStatement statement = mPreparedStatementCache.get(sql);
    boolean skipCache = false;
    if (statement != null) {
        if (!statement.mInUse) {
            return statement;
        }
        // The statement is already in the cache but is in use (this statement appears
        // to be not only re-entrant but recursive!).  So prepare a new copy of the
        // statement but do not cache it.
        skipCache = true;
    }

    final long statementPtr = nativePrepareStatement(mConnectionPtr, sql);
    try {
        final int numParameters = nativeGetParameterCount(mConnectionPtr, statementPtr);
        final int type = DatabaseUtils.getSqlStatementType(sql);
        final boolean readOnly = nativeIsReadOnly(mConnectionPtr, statementPtr);
        statement = obtainPreparedStatement(sql, statementPtr, numParameters, type, readOnly);
        if (!skipCache && isCacheable(type)) {
            mPreparedStatementCache.put(sql, statement);
            statement.mInCache = true;
        }
    } catch (RuntimeException ex) {
        // Finalize the statement if an exception occurred and we di

 

  4、分多独db来实现并发写;

 

  三、getReadableDatabase()和getWritableDatabase()

  getReadableDatabase()和getWritableDatabase()首先都见面尝试以朗诵写方式打开数据库。其中getReadableDatabase()如果坐磁盘空间已满等由致因朗诵写方式打开数据库失败,会改以单纯念方式打开,而getWritableDatabase()会丢掉大。若仅需要一个独念之数据库,可以使SQLiteDatabase.OPEN_READONLY标志,通过SQLiteDatabase#openDatabase(String,
CursorFactory, int)方法手动打开。

  这半只章程成功返回后,会转调onOpen()方法,且OpenHelper会缓存该数据库实例。这有限单艺术调用时,如果坐数据库文件未设有用创造会触发SQLiteOpenHelper#onCreate()回调,如果因数据库版本不一致升或降会触发SQLiteOpenHelper#onUpgrade()、SQLiteOpenHelper#onDowngrade()回调。

 

  差不多就是这些,欢迎大家补充及指正。最后是私有写的SQLite辅助管理类,维护一个SQLiteOpenHelper实例,并提供全局SQLiteDatabase实例打开和关,避免为大多线程操作还是再打开关闭导致的database
is locked、reopen and already
closed等死,并且支持wal模式。因为比较简单,就非做单独教了。

 

public class SQLiteDbManager
{
    private SQLiteDbManager()
    {
    }

    private AtomicInteger mOpenCounter = new AtomicInteger();

    private static SQLiteDbManager mDatabaseHelper;
    private static SQLiteOpenHelper mSQLiteDbMaintain;
    private SQLiteDatabase mDatabase;

    private boolean mEnableWAL;

    public static void initializeInstance(SQLiteOpenHelper dbMaintain, boolean enableWAL)
    {
        if (mDatabaseHelper == null)
        {
            mDatabaseHelper = new SQLiteDbManager();
            mDatabaseHelper.mEnableWAL = enableWAL;

            mSQLiteDbMaintain = dbMaintain;
        }
    }

    public static SQLiteDbManager getInstance()
    {
        return mDatabaseHelper;
    }

    public synchronized SQLiteDatabase openDatabase()
    {
        if (mOpenCounter.incrementAndGet() == 1)
        {
            try
            {
                mDatabase = mSQLiteDbMaintain.getReadableDatabase();

                // 并发读
                if (mEnableWAL && Build.VERSION.SDK_INT >= 11)
                {
                    mDatabase.enableWriteAheadLogging();
                }
            }
            catch (SQLiteException ex)
            {
                mOpenCounter.decrementAndGet();
                mDatabase = null;
                Logger.getInstance().error(ex.toString());
            }
        }
        return mDatabase;
    }

    public void beginTransaction()
    {
        if (Build.VERSION.SDK_INT >= 11 && mEnableWAL)
        {
            mDatabase.beginTransactionNonExclusive();
            return;
        }

        mDatabase.beginTransaction();
    }

    public void beginTransactionWithListener(SQLiteTransactionListener listener)
    {
        if (Build.VERSION.SDK_INT >= 11 && mEnableWAL)
        {
            mDatabase.beginTransactionWithListenerNonExclusive(listener);
            return;
        }

        mDatabase.beginTransactionWithListener(listener);
    }

    public synchronized void closeDatabase()
    {
        if (mOpenCounter.decrementAndGet() == 0)
        {
            mDatabase.close();
        }
    }
}

 

参照链接:

http://www.trinea.cn/android/database-performance/

http://blog.csdn.net/efeics/article/details/18995433

网站地图xml地图