database performance - Android SQLite数据库:插入缓慢




insert (5)

我需要解析一个相当大的XML文件(大约在一百千字节和几百千字节之间),我使用Xml#parse(String, ContentHandler) 。 我目前正在用一个152KB的文件来测试它。

在解析过程中,我还使用类似于以下的调用将数据插入到SQLite数据库中: getWritableDatabase().insert(TABLE_NAME, "_id", values) _ getWritableDatabase().insert(TABLE_NAME, "_id", values) 。 所有这些在一起大约需要80秒,用于152KB的测试文件(可以插入大约200行)。

当我注释掉所有插入语句(但是在其他所有内容中,例如创建ContentValues等)时,同一个文件只需要23秒。

数据库操作有这么大的开销是否正常? 我能做些什么吗?


Answers

如果使用ContentProvider:

@Override
public int bulkInsert(Uri uri, ContentValues[] bulkinsertvalues) {

    int QueryType = sUriMatcher.match(uri);
    int returnValue=0;
    SQLiteDatabase db = mOpenHelper.getWritableDatabase();

     switch (QueryType) {

         case SOME_URI_IM_LOOKING_FOR: //replace this with your real URI

            db.beginTransaction();

            for (int i = 0; i < bulkinsertvalues.length; i++) {
                //get an individual result from the array of ContentValues
                ContentValues values = bulkinsertvalues[i];
                //insert this record into the local SQLite database using a private function you create, "insertIndividualRecord" (replace with a better function name)
                insertIndividualRecord(uri, values);    
            }

            db.setTransactionSuccessful();
            db.endTransaction();                 

            break;  

         default:
             throw new IllegalArgumentException("Unknown URI " + uri);

     }    

    return returnValue;

}

然后执行插入的私有函数(仍然在您的内容提供者中):

       private Uri insertIndividualRecord(Uri uri, ContentValues values){

            //see content provider documentation if this is confusing
            if (sUriMatcher.match(uri) != THE_CONSTANT_IM_LOOKING_FOR) {
                throw new IllegalArgumentException("Unknown URI " + uri);
            }

            //example validation if you have a field called "name" in your database
            if (values.containsKey(YOUR_CONSTANT_FOR_NAME) == false) {
                values.put(YOUR_CONSTANT_FOR_NAME, "");
            }

            //******add all your other validations

            //**********

           //time to insert records into your local SQLite database
           SQLiteDatabase db = mOpenHelper.getWritableDatabase();
           long rowId = db.insert(YOUR_TABLE_NAME, null, values);           

           if (rowId > 0) {
               Uri myUri = ContentUris.withAppendedId(MY_INSERT_URI, rowId);
               getContext().getContentResolver().notifyChange(myUri, null);

               return myUri;
           }


           throw new SQLException("Failed to insert row into " + uri);


    }

由于Yuku和Brett提到的InsertHelper现在已经被deprecated (API级别17),似乎Google推荐的正确选择是使用SQLiteStatement

我使用这样的数据库插入方法:

database.insert(table, null, values);

在我遇到一些严重的性能问题后,下面的代码加快了我的500插入从14.5秒增加到仅270毫秒 ,太棒了!

以下是我如何使用SQLiteStatement:

private void insertTestData() {
    String sql = "insert into producttable (name, description, price, stock_available) values (?, ?, ?, ?);";

    dbHandler.getWritableDatabase();
    database.beginTransaction();
    SQLiteStatement stmt = database.compileStatement(sql);

    for (int i = 0; i < NUMBER_OF_ROWS; i++) {
        //generate some values

        stmt.bindString(1, randomName);
        stmt.bindString(2, randomDescription);
        stmt.bindDouble(3, randomPrice);
        stmt.bindLong(4, randomNumber);

        long entryID = stmt.executeInsert();
        stmt.clearBindings();
    }

    database.setTransactionSuccessful();
    database.endTransaction();

    dbHandler.close();
}

如果表上有一个索引,请考虑在插入记录之前将其删除,然后在提交记录后再将其添加回来。


编译sql插入语句有助于加快速度。 它还需要更多的努力来支撑一切,并防止可能的注射,因为它现在全部放在你的肩上。

另一种可以加快速度的方法是缺少记录的android.database.DatabaseUtils.InsertHelper类。 我的理解是它实际上包装了编译的插入语句。 从非编译的事务插入到编译的事务插入对于我的大型(200K +条目)而言简单的SQLite插入,速度增加了3倍(每插入2毫秒,每插入0.6毫秒)。

示例代码:

SQLiteDatabse db = getWriteableDatabase();

//use the db you would normally use for db.insert, and the "table_name"
//is the same one you would use in db.insert()
InsertHelper iHelp = new InsertHelper(db, "table_name");

//Get the indices you need to bind data to
//Similar to Cursor.getColumnIndex("col_name");                 
int first_index = iHelp.getColumnIndex("first");
int last_index = iHelp.getColumnIndex("last");

try
{
   db.beginTransaction();
   for(int i=0 ; i<num_things ; ++i)
   {
       //need to tell the helper you are inserting (rather than replacing)
       iHelp.prepareForInsert();

       //do the equivalent of ContentValues.put("field","value") here
       iHelp.bind(first_index, thing_1);
       iHelp.bind(last_index, thing_2);

       //the db.insert() equilvalent
       iHelp.execute();
   }
   db.setTransactionSuccessful();
}
finally
{
    db.endTransaction();
}
db.close();

在语音识别学科中,用户分为山羊绵羊

例如,请参见第89页

绵羊是语音识别非常好的人,山羊是非常糟糕的人。 只有语音识别器知道它们之间的区别。 人们无法预测谁的声音将被轻易识别,谁的声音不会被识别。 最好的策略是设计界面,以便它可以处理各种环境中的各种声音

也许,计划在未来将Android用户标记为山羊,以便能够配置语音识别引擎以满足山羊的需求。 ;-)





android database performance sqlite insert