Android: Optimización de accesos en SQLite

Os regalo un snippet que os va a volver locos a todos los que desarrolléis una aplicación que tenga un acceso intenso a datos en SQL.

Está sacado de un bugfix incluido en Mozilla Firefox para Android y yo he comprobado en mis carnes el sustancial aumento de rendimiento, en vuestro SQLiteOpenHelper, añadir:



@Override public void onOpen(SQLiteDatabase db) {
// From Honeycomb on, it's possible to run several db
// commands in parallel using multiple connections.
if (Build.VERSION.SDK_INT >= 11) {
db.enableWriteAheadLogging();
}
else
{
// Pre-Honeycomb, we can do some lesser optimizations.
Cursor cursor = null;
try {
cursor = db.rawQuery("PRAGMA synchronous=NORMAL", null);
} finally {
if (cursor != null)
cursor.close();
} cursor = null;
try {
cursor = db.rawQuery("PRAGMA journal_mode=PERSIST", null);
} finally {
if (cursor != null)
cursor.close();
}
}
super.onOpen(db);
}

Según las pruebas de aumento de rendimiento de la gente de Mozilla Firefox, esto por si solo provoca un aumento el 50% de rendimiento y la razón es que por defecto SQLite tiene marcado el pragma synchronous a FULL lo que quiere decir que en cada acceso a la base de datos realiza un bloqueo completo para garantizar que no provoca ningún problema de corrupción.


http://www.sqlite.org/pragma.html
PRAGMA synchronous = NORMAL: "When synchronous is NORMAL (1), the SQLite database engine will still sync at the most critical moments, but less often than in FULL mode. There is a very small (though non-zero) chance that a power failure at just the wrong time could corrupt the database in NORMAL mode. But in practice, you are more likely to suffer a catastrophic disk failure or some other unrecoverable hardware fault."


Para obtener una corrupción en modo NORMAL necesitas una colisión en un checksum de 32-bits por lo que me parece razonablemente descartable la posibilidad xD.

Además se pone el modo del journaling en PERSIST:

PRAGMA journal = PERSIST:
Deleting a file is an expensive operation on many systems. So as an optimization, SQLite can be configured to avoid the delete operation of section 3.11. Instead of deleting the journal file in order to commit a transaction, the file is either truncated to zero bytes in length or its header is overwritten with zeros. Truncating the file to zero length saves having to make modifications to the directory containing the file since the file is not removed from the directory. Overwriting the header has the additional savings of not having to update the length of the file (in the "inode" on many systems) and not having to deal with newly freed disk sectors. Furthermore, at the next transaction the journal will be created by overwriting existing content rather than appending new content onto the end of a file, and overwriting is often much faster than appending.


La gente de Mozilla Firefox ha verificado que en Android es mucho mas lento el comportamiento de TRUNCATE que el de PERSIST; en muchos sistemas embedidos con sistemas de archivos sincronos ocurrirá lo mismo.

Como veis para dispositivos modernos simplemente se activa el Write Ahead Logging, os recomiendo que miréis que es en profundidad pero groso modo podemos decir que se permite leer sin bloqueo y solo se usa el bloqueo para escrituras ( si he dicho una burrada, por favor corregidme ).

Si tenéis algun acceso MUY BRUTO a la base de datos ( imagina insertar 100.000 filas de golpe ); deberíais ( solo para ese acceso ) usar setPerformanceMode que activa el shynchronous a NONE hacer una transacción como dios manda y luego volver a dejarlo como estaba.

Happy coding!

Comentarios

Entradas populares de este blog

VS2008 C# Centrar un formulario en pantalla.

VS2008 C# Ocultar la primera fila de un DataGridView ( solución para cuando no se deja )

VS2008 C# ComboBox enlazado a datos con primer elemento en blanco