2011-06-15 10 views
7

Estoy usando el almacenamiento externo para almacenar eventos en una base de datos mientras esperan ser enviados al servidor.Mal rendimiento de SQLite en almacenamiento externo en Android

Estoy viendo un rendimiento muy malo al insertar registros. Sé que la memoria externa puede ser lenta, pero quería ver un número así que escribí una pequeña aplicación que lo prueba.

Aquí está el código:

public static final int INSERTS = 100; 

File dbFile = new File(Environment.getExternalStorageDirectory(), "test.sqlite3"); 
// File dbFile = new File(getFilesDir(), "test.sqlite3"); 
dbFile.delete(); 

SQLiteDatabase db = SQLiteDatabase.openOrCreateDatabase(dbFile, null); 

db.execSQL("CREATE TABLE events (_id integer primary key autoincrement, event_type TEXT NOT NULL, timestamp BIGINT, data TEXT);"); 
db.execSQL("CREATE INDEX mainIndex ON events (event_type, timestamp ASC);"); 

InsertHelper helper = new InsertHelper(db, "events"); 

final int eventTypeCol = helper.getColumnIndex("event_type"); 
final int timestampCol = helper.getColumnIndex("timestamp"); 
final int dataCol = helper.getColumnIndex("data"); 

long start = System.currentTimeMillis(); 

String eventType = "foo", data = "bar"; 
long timestamp = 4711; 

for(int i = 0; i < INSERTS; ++i) { 
    helper.prepareForInsert(); 
    helper.bind(eventTypeCol, eventType); 
    helper.bind(timestampCol, timestamp); 
    helper.bind(dataCol, data); 
    helper.execute(); 
} 

long end = System.currentTimeMillis(); 

Log.i("Test", String.format("InsertHelper, Speed: %d ms, Records per second: %.2f", (int)(end-start), 1000*(double)INSERTS/(double)(end-start))); 

db.close(); 
dbFile.delete(); 

db = SQLiteDatabase.openOrCreateDatabase(dbFile, null); 

db.execSQL("CREATE TABLE events (_id integer primary key autoincrement, event_type TEXT NOT NULL, timestamp BIGINT, data TEXT);"); 
db.execSQL("CREATE INDEX mainIndex ON events (event_type, timestamp ASC);"); 


start = System.currentTimeMillis(); 
ContentValues cv = new ContentValues(); 

for(int i = 0; i < INSERTS; ++i) { 
    cv.put("event_type", eventType); 
    cv.put("timestamp", timestamp); 
    cv.put("data", data); 
    db.insert("events", null, cv); 
} 

end = System.currentTimeMillis(); 

Log.i("Test", String.format("Normal, Speed: %d ms, Records per second: %.2f", end-start, 1000*(double)INSERTS/(double)(end-start))); 

db.close(); 
dbFile.delete(); 

La base de datos es exactamente como la que mi verdadera aplicación está utilizando, que intentó eliminar el índice, pero no había ninguna diferencia.

Éstos son los resultados:

 
Nexus One, Internal memory 

     Method | Records | Time (ms) | Records per second 
-------------+---------+-----------+-------------------- 
     Normal | 100 | 2072 |  48.26 
InsertHelper | 100 | 1662 |  60.17 


Nexus One, External memory: 

     Method | Records | Time (ms) | Records per second 
-------------+---------+-----------+-------------------- 
     Normal | 100 | 7390 |  13.53 
InsertHelper | 100 | 7152 |  13.98 


Emulator, Internal memory: 

     Method | Records | Time (ms) | Records per second 
-------------+---------+-----------+-------------------- 
     Normal | 100 | 1803 |  55.46 
InsertHelper | 100 | 3075 |  32.52 


Emulator, External memory: 

     Method | Records | Time (ms) | Records per second 
-------------+---------+-----------+-------------------- 
     Normal | 100 | 5742 |  17.42 
InsertHelper | 100 | 7164 |  13.96 

Como se puede ver el emulador no se puede confiar, InsertHelper debería ser más rápido si cabe.
Esto es, por supuesto, de esperar, la prueba se realizó principalmente por curiosidad.

Lo que me preocupa, sin embargo, es el mal funcionamiento en mi teléfono cuando uso memoria externa, ¿he olvidado algún aspecto crucial de SQLiteDatabase o simplemente es para que la tarjeta SD sea lenta?

Puedo agregar que en mi aplicación real he desactivado locking y hace poca diferencia.

+1

A menos que tenga una muy buena razón para poner en su base de datos de almacenamiento externo, yo no haría eso. El almacenamiento externo en Android 1.xy 2.x no siempre está disponible (por ejemplo, el usuario monta el almacenamiento como una unidad en su computadora de escritorio o portátil). Además, para medir las operaciones de E/S vinculadas, el emulador tendrá poca similitud con el hardware de producción, ya que el emulador no utiliza el flash para nada (interno o externo), sino que está utilizando archivos de imagen de disco en su máquina de desarrollo. – CommonsWare

+2

El problema es que la base de datos podría crecer, probablemente tendrá un límite máximo de aproximadamente 10 MB o más, no quiero ocupar tanto espacio internamente. –

Respuesta

13

CommonsWare tiene razón en su comentario. Algo que hace una gran diferencia para el rendimiento de DB es el uso de transacciones. Envuelva su bucle de inserción en una transacción. No estoy 100% seguro de si funcionaría con el InsertHelper pero se puede intentar sustituir el bucle for con esto:

db.beginTransaction(); 
try { 
    for(int i = 0; i < INSERTS; ++i) { 
     helper.prepareForInsert(); 
     helper.bind(eventTypeCol, eventType); 
     helper.bind(timestampCol, timestamp); 
     helper.bind(dataCol, data); 
     helper.execute(); 
    } 
    db.setTransactionSuccessful(); 
} finally { 
    db.endTransaction(); 
} 
+0

Las transacciones me dieron 925 registros por segundo, increíble :) –

+3

En realidad, 'InsertHelper.execute()' usa valores de retorno en lugar de excepciones, por lo que debe verificar si devuelve -1 en su lugar. –

2

que tienen algunos problemas de rendimiento db por lo que utiliza el código para medir las inserciones por segundo en mi sistema Pero también agregué el ajuste en {begin, end} Transaction().

En el emulador. Tengo:

InsertHelper-Internal-Trans, Speed: 67 ms, Records per second: 1492.54 
InsertHelper-External-Trans, Speed: 70 ms, Records per second: 1428.57 
Normal-Internal-Trans, Speed: 148 ms, Records per second: 675.68 
Normal-External-Trans, Speed: 152 ms, Records per second: 657.89 
InsertHelper-Internal-NoTrans, Speed: 514 ms, Records per second: 194.55 
Normal-Internal-NoTrans, Speed: 519 ms, Records per second: 192.68 
InsertHelper-External-NoTrans, Speed: 590 ms, Records per second: 169.49 
Normal-External-NoTrans, Speed: 618 ms, Records per second: 161.81 

Y en un Samsung Galaxy Note:

InsertHelper-External-Trans, Speed: 52 ms, Records per second: 1923.08 
InsertHelper-Internal-Trans, Speed: 52 ms, Records per second: 1923.08 
Normal-External-Trans, Speed: 77 ms, Records per second: 1298.70 
Normal-Internal-Trans, Speed: 121 ms, Records per second: 826.45 
Normal-External-NoTrans, Speed: 4562 ms, Records per second: 21.92 
Normal-Internal-NoTrans, Speed: 4855 ms, Records per second: 20.60 
InsertHelper-External-NoTrans, Speed: 5997 ms, Records per second: 16.68 
InsertHelper-Internal-NoTrans, Speed: 8361 ms, Records per second: 11.96 
Cuestiones relacionadas