performance - En SQLite, ¿las declaraciones preparadas realmente mejoran el rendimiento?
sqlite performance (3)
Las declaraciones preparadas mejoran el rendimiento al almacenar en caché el plan de ejecución para una consulta una vez que el optimizador de consultas ha encontrado el mejor plan.
Si la consulta que está utilizando no tiene un plan complicado (como simples selecciones / inserciones sin uniones), las declaraciones preparadas no le darán una gran mejora ya que el optimizador encontrará rápidamente el mejor plan.
Sin embargo, si ejecutaba la misma prueba con una consulta que tenía algunas combinaciones y usaba algunos índices, vería la diferencia de rendimiento, ya que el optimizador no se ejecutaría cada vez que se realizara la consulta.
He oído que las declaraciones preparadas con SQLite deberían mejorar el rendimiento. Escribí un código para probar eso, y no vi ninguna diferencia en el rendimiento con el uso de ellos. Entonces, pensé que tal vez mi código era incorrecto. Por favor, avíseme si ve algún error en cómo estoy haciendo esto ...
[self testPrep:NO dbConn:dbConn];
[self testPrep:YES dbConn:dbConn];
reuse=0
recs=2000
2009-11-09 10:39:18 -0800
processing...
2009-11-09 10:39:32 -0800
reuse=1
recs=2000
2009-11-09 10:39:32 -0800
processing...
2009-11-09 10:39:46 -0800
-(void)testPrep:(BOOL)reuse dbConn:(sqlite3*)dbConn{
int recs = 2000;
NSString *sql;
sqlite3_stmt *stmt;
sql = @"DROP TABLE test";
sqlite3_exec(dbConn, [sql UTF8String],NULL,NULL,NULL);
sql = @"CREATE TABLE test (id INT,field1 INT, field2 INT,field3 INT,field4 INT,field5 INT,field6 INT,field7 INT,field8 INT,field9 INT,field10 INT)";
sqlite3_exec(dbConn, [sql UTF8String],NULL,NULL,NULL);
for(int i=0;i<recs;i++){
sql = @"INSERT INTO test (id,field1,field2,field3,field4,field5,field6,field7,field8,field9,field10) VALUES (%d,1,2,3,4,5,6,7,8,9,10)";
sqlite3_exec(dbConn, [sql UTF8String],NULL,NULL,NULL);
}
sql = @"BEGIN";
sqlite3_exec(dbConn, [sql UTF8String],NULL,NULL,NULL);
if (reuse){
sql = @"select * from test where field1=?1 and field2=?2 and field3=?3 and field4=?4 and field5=?5 and field6=?6 and field6=?6 and field8=?8 and field9=?9 and field10=?10 and id=?11";
sqlite3_prepare_v2(dbConn, [sql UTF8String], -1, &stmt, NULL);
}
NSLog(@"reuse=%d",reuse);
NSLog(@"recs=%d",recs);
NSDate *before = [NSDate date];
NSLog([before description]);
NSLog(@"processing...");
for(int i=0;i<recs;i++){
if (!reuse){
sql = @"select * from test where field1=?1 and field2=?2 and field3=?3 and field4=?4 and field5=?5 and field6=?6 and field6=?6 and field8=?8 and field9=?9 and field10=?10 and id=?11";
sqlite3_prepare_v2(dbConn, [sql UTF8String], -1, &stmt, NULL);
}
sqlite3_bind_int(stmt, 1, 1);
sqlite3_bind_int(stmt, 2, 2);
sqlite3_bind_int(stmt, 3, 3);
sqlite3_bind_int(stmt, 4, 4);
sqlite3_bind_int(stmt, 5, 5);
sqlite3_bind_int(stmt, 6, 6);
sqlite3_bind_int(stmt, 7, 7);
sqlite3_bind_int(stmt, 8, 8);
sqlite3_bind_int(stmt, 9, 9);
sqlite3_bind_int(stmt, 10, 10);
sqlite3_bind_int(stmt, 11, i);
while(sqlite3_step(stmt) == SQLITE_ROW) {
}
sqlite3_reset(stmt);
}
sql = @"BEGIN";
sqlite3_exec(dbConn, [sql UTF8String],NULL,NULL,NULL);
NSDate *after = [NSDate date];
NSLog([after description]);
}
Sí, hace una gran diferencia si usa sqlite3_exec()
frente a sqlite3_prepare_v2() / sqlite3_bind_xxx() / sqlite3_step()
para inserciones masivas.
sqlite3_exec()
es solo un método de conveniencia. Internamente solo llama a la misma secuencia de sqlite3_prepare_v2() and sqlite3_step()
. Su código de ejemplo llama a sqlite3_exec()
una y otra vez en una cadena literal:
for(int i=0;i<recs;i++){
sql = @"INSERT INTO test (id,field1,field2,field3,field4,field5,field6,field7,field8,field9,field10) VALUES (%d,1,2,3,4,5,6,7,8,9,10)";
sqlite3_exec(dbConn, [sql UTF8String],NULL,NULL,NULL);
}
No conozco el funcionamiento interno del analizador SQLite, pero tal vez el analizador sea lo suficientemente inteligente como para reconocer que está utilizando la misma cadena literal y luego omite el re-análisis / compilación con cada iteración.
Si prueba el mismo experimento con valores que cambian, verá una diferencia mucho mayor en el rendimiento.
Usar el paso de preparación + en lugar de ejecutar grandes mejoras de rendimiento es posible. En algunos casos, la ganancia de rendimiento es más del 100% en tiempo de ejecución.