Base de datos SQLite de Android: inserción lenta

Tengo que analizar un archivo XML bastante grande (que varía entre unos cien kilobytes y varios cientos de kilobytes), que estoy haciendo usando Xml#parse(String, ContentHandler) . Actualmente estoy probando esto con un archivo de 152 KB.

Durante el análisis, también inserto los datos en una base de datos SQLite utilizando llamadas similares a las siguientes: getWritableDatabase().insert(TABLE_NAME, "_id", values) . Todo esto en conjunto toma unos 80 segundos para el archivo de prueba de 152 KB (que se reduce a insertar aproximadamente 200 filas).

Cuando hago comentarios sobre todas las sentencias de inserción (pero dejo en todo lo demás, como la creación de ContentValues etc.), el mismo archivo tarda sólo 23 segundos.

¿Es normal que las operaciones de la base de datos tengan una sobrecarga tan grande? ¿Puedo hacer algo al respecto?

Debe hacer inserciones de lote.

Pseudocódigo:

 db.beginTransaction(); for (entry : listOfEntries) { db.insert(entry); } db.setTransactionSuccessful(); db.endTransaction(); 

Que aumentó la velocidad de inserciones en mis aplicaciones extremadamente.

Actualizar:
@ Yuku siempre una entrada de blog muy interesante: Android con inserthelper para inserciones más rápidas en la base de datos sqlite

Dado que el InsertHelper mencionado por Yuku y Brett está ahora en desuso (nivel API 17), parece que la alternativa correcta recomendada por Google es usar SQLiteStatement .

He utilizado el método de inserción de base de datos como este:

 database.insert(table, null, values); 

Después de que también experimenté algunos problemas graves de rendimiento, el código siguiente aceleró mis inserciones 500 de 14,5 segundos a sólo 270 ms , increíble!

Aquí es cómo usé 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(); } 

La compilación de la instrucción de inserto de sql ayuda a acelerar las cosas. También puede requerir más esfuerzo para apuntalar todo y evitar la posible inyección, ya que ahora es todo en los hombros.

Otro enfoque que también puede acelerar las cosas es la clase android.database.DatabaseUtils.InsertHelper bajo-documentada. Mi comprensión es que en realidad envuelve las instrucciones de inserción compiladas. Pasando de inserciones transaccionadas no compiladas a inserciones transaccionadas compiladas, se trataba de una ganancia de velocidad 3x (2ms por inserto a .6ms por inserto) para mis inserciones SQLite grandes (200K + entradas).

Código de muestra:

 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(); 

Si la tabla tiene un índice en ella, considere dejarla antes de insertar los registros y, a continuación, volver a agregar después de haber comprometido sus registros.

Si utiliza un 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; } 

A continuación, la función privada para realizar la inserción (aún dentro de su proveedor de contenido):

  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); } 
  • Cómo utilizar DefaultDatabaseErrorHandler para controlar la corrupción de base de datos en Android
  • Enfoque para rellenar la vista de lista expandible con la base de datos SQlite local
  • SQLite Android no puede abrir el archivo de base de datos
  • Cómo utilizar la consulta para el argumento de selección múltiple utilizando una columna de selección
  • SQLite Android. No puede abrir el archivo de base de datos
  • Especificar el tamaño de sqlite db máximo para mi aplicación. Cómo manejar db completa excepción
  • Android: carga el contenido de la base de datos sqlite en webview
  • INSERT de SQLite de SQL
  • SimpleCursorAdapter de Android con consultas utilizando DISTINCT
  • Android: utilice la base de datos SQLite en la tarjeta SD (no utilizando el almacén de datos Android interno)
  • No se copia la base de datos de la carpeta de activos al dispositivo
  • FlipAndroid es un fan de Google para Android, Todo sobre Android Phones, Android Wear, Android Dev y Aplicaciones para Android Aplicaciones.