sqliteAndroid SQLite计算

  SQLite在Android一般选用中依然相比较常用,早期的时候遇到过不少坑,当中最烦的正是四线程并发读写标题,明日恰巧整理一下,做个笔记,也欢迎指正、钻探和互补。

  一 、查询优化

  1、wal模式

  开启wal情势,能够实现并发读,且读写不打断,当然写与写之间照旧阻塞,该格局需求android3.0+才支撑。

  当打开了wal方式立异数据时,会先将数据写入到*.db-wal文件中,而不是直接修改数据库文件,当执行checkpoint时或有个别时刻点才会将数据更新到数据库文件(执行endTransaction时会提交checkpoint)。当出现rollback也只是割除wal日志文件,而ROLLBACK
JOU奥迪Q5NAL方式,也正是关门wal情势时,当数码有创新时,先将须求修改的数据备份到journal文件中,然后修改数据库文件,当产生rollback,从journal日志中取出数据,并修改数据库文件,然后去掉journal日志。
从以上流程来看wal在数码更新上I/O量要小,所以写操作要快。由于在读取数据时也急需读取wal日志验证数据的正确性,所以读取数据相对要慢,但运用wal依旧增强了读取的并发性。

  开启wal形式后,一定要使用beginTransactionNonExclusive来交付业务。db.beginTransaction()约等于execSQL(“BEGIN
EXCLUSIVE;”),在眼下业务没有甘休从前其余别的线程或进程都没办法儿对数据库进行读写操作。当打开wal形式时,使用db.beginTransactionNonExclusive(),相当于execSQL(“BEGIN
IMMEDIATE;”),只会限制别的线程对数据库的写操作,不会阻塞读操作。

  二 、建立目录,推荐看这一个稿子,丰盛驾驭索引的简练利用和优点了http://www.trinea.cn/android/database-performance/,一句话来说,索引会扩张SQLite体量,且增加和删除改时也要保证索引,会对增加和删除改品质存在一定影响,假诺数据量相当小,不提议采纳。使用时肯定要根据须要建立适合的目录,勿滥用。

  ③ 、当某张表可预知数据量一点都不小时,能够适当的开始展览表的细化、中期能够分表分库,查询时也得以应用异步查询。

 

  贰 、批量插入优化

  一 、事务提交

  批量安顿,包罗创新删除,一定要加事务,假设不加事务,则暗许会为每1次插入开启三个政工并自行提交,是卓殊慢的。

  二 、开启wal形式,参见上文中解释;

  3、SQLiteStatement优化

  大家每便执行的sql语句最后会转接为叁个SQLiteStatement对象来开始展览拍卖,能够预先使用db.compileStatement方法获得SQLiteStatement对象并选用,而不是让系统每便insert都组织三个对应的SQLiteStatement对象,那样能够增长内部存款和储蓄器的使用率。

  补充:网上有人解释“比如insert into
xxx,一般情况下执行多少次,就要编译多少次
”,关于那一点,首先自己觉得畸形,小编演说一下自个儿自个儿的解析:SQLite想要执行操作,要求将次第中的sql语句实行“预编写翻译”。例如批量布署,我们得以行使“显式预编写翻译”来形成重用SQLiteStatement,也正是使用compileStatement方法。其实首要在于SQLiteStatement对象在new时,会通过SQLiteSession获取连接池中某些SQLiteConnection,通过调用SQLiteConnection的prepare方法,会从PreparedStatement链表中获得,假如没有可选择的则会创立3个PreparedStatement对象,个中会做一些native操作,例如给PreparedStatement的mStatementPtr赋值,通过注释,大家能够精晓到那么些mStatementPtr便是1个针对sqlite3_stmt类型的指针,而sqlite3_stmt是sqlite自个儿之中的数据结构,用来记录“sql语句”,那一个sql语句是分析后的,也即是“预编写翻译”后的。

  一句话,就是new
SQLiteStatement会对sql做预编写翻译,固然已经预编写翻译过,会直接从缓存链表中拿。

  其实从地方分析,我们领略各样SQLiteConnection都带有3个链表结构的PreparedStatemnt对象集合,每便得到SQLiteConnection都会先行找到包罗sql预编写翻译的PreparedStatement实例的数据库连接,那样就不会每趟都去预编写翻译sql。所以唯有那个connection刚好被其余线程拿去用了,不然都取得相同的connection,不用再行预编写翻译。也正是说在曾经履行过1回预编写翻译(生成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

 

  四 、分五个db来促成并发写;

 

  三、getReadableDatabase()和getWritableDatabase()

  getReadableDatabase()和getWritableDatabase()首先都会尝试以读写方式打开数据库。个中getReadableDatabase()假诺因为磁盘空间已满等原因造成以读写格局打开数据库失利,会改以只读方式打开,而getWritableDatabase()会抛相当。若只必要1个只读的数据库,可以运用SQLiteDatabase.OPEN_READONLY标志,通过SQLiteDatabase#sqlite,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地图