2012-05-04 31 views
6

Conclusión
Problema cerrado, creo.
Parece que el problema no tuvo nada que ver con la metodología, pero que el XCode no limpió el proyecto correctamente entre compilaciones.
Parece que después de todas esas pruebas, el archivo sqlite que se estaba utilizando seguía siendo el primero que no se indexó ......
Tenga cuidado con XCode 4.3.2, no tengo más que problemas con Clean no limpiar, o la adición de archivos de proyecto no se añade automáticamente a los recursos de paquete ...
Gracias por las diferentes respuestas ..¿Cuál es la forma más rápida de cargar un gran archivo CSV en los datos centrales

Actualizar 3
Desde invito a cualquiera a sólo tratar los mismos pasos para ver si obtienen los mismos resultados, déjenme detallar lo que hice:
Comienzo con proyecto en blanco
que definen un modelo de datos con una sola entidad, 3 atributos (2 cadenas, 1 float)
La primera cadena está indexado
enter image description here

En qué finishLaunchingWithOptions, estoy llamando:

[self performSelectorInBackground:@selector(populateDB) withObject:nil]; 

El código para populateDb está abajo:

-(void)populateDB{ 
NSLog(@"start"); 
NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator]; 
NSManagedObjectContext *context; 
if (coordinator != nil) { 
    context = [[NSManagedObjectContext alloc] init]; 
    [context setPersistentStoreCoordinator:coordinator]; 
} 

NSString *filePath = [[NSBundle mainBundle] pathForResource:@"input" ofType:@"txt"]; 
if (filePath) { 
    NSString * myText = [[NSString alloc] 
           initWithContentsOfFile:filePath 
           encoding:NSUTF8StringEncoding 
           error:nil]; 
    if (myText) { 
     __block int count = 0; 


     [myText enumerateLinesUsingBlock:^(NSString * line, BOOL * stop) { 
      line=[line stringByReplacingOccurrencesOfString:@"\t" withString:@" "]; 
      NSArray *lineComponents=[line componentsSeparatedByString:@" "]; 
      if(lineComponents){ 
       if([lineComponents count]==3){ 
        float f=[[lineComponents objectAtIndex:0] floatValue]; 
        NSNumber *number=[NSNumber numberWithFloat:f]; 
        NSString *string1=[lineComponents objectAtIndex:1]; 
        NSString *string2=[lineComponents objectAtIndex:2]; 
        NSManagedObject *object=[NSEntityDescription insertNewObjectForEntityForName:@"Bigram" inManagedObjectContext:context]; 
        [object setValue:number forKey:@"number"]; 
        [object setValue:string1 forKey:@"string1"]; 
        [object setValue:string2 forKey:@"string2"]; 
        NSError *error; 
        count++; 
        if(count>=1000){ 
         if (![context save:&error]) { 
          NSLog(@"Whoops, couldn't save: %@", [error localizedDescription]); 
         } 
         count=0; 

        } 
       } 
      } 



     }]; 
     NSLog(@"done importing"); 
     NSError *error; 
     if (![context save:&error]) { 
      NSLog(@"Whoops, couldn't save: %@", [error localizedDescription]); 
     } 

    } 
} 
NSLog(@"end"); 
} 

Todo lo demás es código de datos del núcleo por defecto, nada añadido.
Lo ejecuto en el simulador.
voy a ~/Library/Application Support/iPhone Simulator/5.1/Aplicaciones // Documentos
existe el archivo de SQLite que se genera

tomo eso y lo copio en mi paquete

Me comentario el llamado a populateDb

edito persistentStoreCoordinator para copiar el archivo de SQLite paquete de documentos a la primera carrera

- (NSPersistentStoreCoordinator *)persistentStoreCoordinator 
{ 
@synchronized (self) 
{ 
    if (__persistentStoreCoordinator != nil) 
     return __persistentStoreCoordinator; 

    NSString *defaultStorePath = [[NSBundle mainBundle] pathForResource:@"myProject" ofType:@"sqlite"]; 
    NSString *storePath = [[[self applicationDocumentsDirectory] path] stringByAppendingPathComponent: @"myProject.sqlite"]; 

    NSError *error; 
    if (![[NSFileManager defaultManager] fileExistsAtPath:storePath]) 
    { 
     if ([[NSFileManager defaultManager] copyItemAtPath:defaultStorePath toPath:storePath error:&error]) 
      NSLog(@"Copied starting data to %@", storePath); 
     else 
      NSLog(@"Error copying default DB to %@ (%@)", storePath, error); 
    } 

    NSURL *storeURL = [NSURL fileURLWithPath:storePath]; 

    __persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:[self managedObjectModel]]; 

    NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys: 
          [NSNumber numberWithBool:YES], NSMigratePersistentStoresAutomaticallyOption, 
          [NSNumber numberWithBool:YES], NSInferMappingModelAutomaticallyOption, nil]; 

    if (![__persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL options:options error:&error]) 
    { 

     NSLog(@"Unresolved error %@, %@", error, [error userInfo]); 
     abort(); 
    }  

    return __persistentStoreCoordinator; 
}  
} 


Elimino la aplicación del simulador, comprobé que ~/Library/Application Support/iPhone Simulator/5.1/Applications/ahora se eliminó
Reconstruyo y lance de nuevo
Como era de esperar, el archivo sqlite se copia en ~/Library/Application Support/iPhone Simulator/5.1/Aplicaciones // Documentos

Sin embargo, el tamaño del archivo es más pequeño que en el paquete, ¡significativamente! También, haciendo una consulta simple con un predicado como este predicado = [NSPredicate predicateWithFormat: @ "string1 ==% @", string1]; muestra claramente que cadena1 no está indexado más

Después de eso, se crea una nueva versión del modelo de datos, con una actualización sin sentido, sólo para forzar una migración de peso ligero
si se ejecuta en el simulador, la migración tiene unos pocos segundos, la base de datos se duplica en tamaño y la misma consulta ahora demora menos de un segundo en vez de minutos.
Esto resolvería mi problema, forzaría una migración, pero esa misma migración lleva 3 minutos en el iPad y ocurre en primer plano.
Así que, donde estoy en este momento, la mejor solución para mí sería evitar que los índices se eliminen, cualquier otra solución de importación en el momento del lanzamiento simplemente lleva demasiado tiempo.
Déjeme saber si usted necesita más aclaraciones ...

Actualización 2
lo tanto, el mejor resultado que he tenido hasta ahora es sembrar la base de datos central de datos con el archivo de SQLite producido a partir de una herramienta rápida con similares modelo de datos, pero sin los índices establecidos al producir el archivo sqlite. Luego, importo este archivo sqlite en la aplicación de datos central con los índices establecidos y permitiendo una migración ligera. Para grabar 2 millones en el nuevo iPad, esta migración toma 3 minutos. La aplicación final debe tener 5 veces este número de registros, por lo que todavía estamos buscando un largo tiempo de procesamiento. Si tomo esa ruta, la nueva pregunta sería: ¿se puede realizar una migración ligera en segundo plano?

actualización
mi pregunta no es cómo crear una herramienta para poblar una base de datos central de datos y, a continuación, importar el archivo de SQLite en mi aplicación.
Sé cómo hacer esto, lo he hecho en innumerables ocasiones.
Pero hasta ahora, no me había dado cuenta de que dicho método podría tener algún efecto secundario: en mi caso, un atributo indexado en la base de datos resultante claramente 'no indexado' al importar el archivo sqlite de esa manera.
Si pudo verificar que los datos indexados aún están indexados después de dicha transferencia, me interesa saber cómo procede o, de lo contrario, cuál sería la mejor estrategia para generar dicha base de datos de manera eficiente.

original

Tengo un archivo CSV grande (millones de líneas) con 4 columnas, cuerdas y flotadores. Esto es para una aplicación iOS.

Necesito que esto se cargue en los datos principales la primera vez que se carga la aplicación.

La aplicación no funciona mucho hasta que los datos están disponibles, por lo que el tiempo de carga es importante, ya que el usuario por primera vez obviamente no quiere que la aplicación tarde 20 minutos en cargarse antes de poder ejecutarla.

En este momento, mi código actual demora 20 minutos en el nuevo iPad para procesar un archivo csv de 2 millones de líneas.

estoy usando un contexto de fondo para no bloquear la interfaz de usuario, y guardar el contexto cada 1.000 registros

La primera idea que tuve fue generar la base de datos en el simulador, a continuación, copiar/pegar en la carpeta de documentos en primer lanzamiento, ya que esta es la forma común no oficial de sembrar una gran base de datos. Desafortunadamente, los índices no parecen sobrevivir a dicha transferencia, y aunque la base de datos estuvo disponible después de unos pocos segundos, el rendimiento es terrible porque mis índices se perdieron. Ya publiqué una pregunta sobre los índices, pero no parece haber una buena respuesta para eso.

Así que lo que estoy buscando, ya sea:

  • una manera de mejorar el rendimiento en la carga de millones de registros de datos básicos
  • si la base de datos está pre-cargado y se trasladó a primera puesta en marcha, de manera para mantener mis índices
  • mejores prácticas para manejar este tipo de situaciones.No recuerdo haber usado ninguna aplicación que requiera que espere x minutos antes del primer uso (pero tal vez The Daily, y esa fue una experiencia terrible).
  • Cualquier forma creativa de hacer que el usuario espere sin que él se dé cuenta: importación en segundo plano al pasar por el tutorial, etc ...
  • ¿No está utilizando Core Data?
  • ...
+0

Entonces, ¿cómo terminaste "limpiando" el proyecto para que funcionara correctamente? – lnafziger

+0

La limpieza no funcionó, pero el reinicio del equipo portátil, la limpieza manual de todas las referencias al archivo, etc., parece haber "resuelto" el problema. extraño ... aunque también tuve que quitar las líneas de migración livianas para forzarme a no migrar (ya que esto tomaría muchos minutos). En general, esta no es la implementación limpia que esperaría, pero esto funciona ... hasta que una versión 2 necesite una actualización de modelo de datos, entonces estoy en problemas –

Respuesta

6

Pre-generar la base de datos usando una aplicación fuera de línea (por ejemplo, una utilidad de línea de comandos), escrito en Cocoa, que se ejecuta en OS X, y utiliza el mismo marco básico de datos que utiliza iOS . No tiene que preocuparse por los "índices que sobreviven" ni nada por el estilo: el resultado es un archivo de base de datos .sqlite generado por los datos centrales, que se puede utilizar de manera inmediata e inmediata en una aplicación de iOS.

Siempre que pueda hacer la generación DB off-line, es la mejor solución con diferencia. Utilicé con éxito esta técnica en bases de datos pregeneradas para el despliegue de iOS. Verifique mis preguntas/respuestas anteriores para un poco más de detalle.

+0

Yo también he hecho lo mismo sin problemas de índice ... – lnafziger

+0

¿Qué quiere decir? No necesito preocuparme por mis índices; como dije en mi pregunta, hice este método exacto, el resultado fue un archivo de base de datos sqlite (200Mb), y cuando se utilizó en mi aplicación con el mismo modelo exacto, el archivo bajó a 110Mb y el rendimiento claramente sugirió que mis índices no trabajando. Entonces SI me preocupo por mis índices, ¡este es el punto! –

+0

@nafziger, ¿quiere decir que tenía índices en su modelo de datos central y que está seguro de que esos índices siguen funcionando como deberían hacerlo una vez que reutiliza ese archivo sqlite? De ser así, ¿cuál era su metodología para asegurarse de que sus índices todavía funcionaran? –

0

Estoy comenzando con SQLite y necesito integrar una base de datos en una de mis aplicaciones que tendrá una gran cantidad de datos indexados en una base de datos SQLite. Esperaba poder hacer algún método donde pudiera insertar mi información a granel en un archivo SQLite y agregar ese archivo a mi proyecto. Después de descubrir y leer su pregunta, la respuesta proporcionada y los numerosos comentarios, decidí consultar la fuente de SQLite para ver si podía llegar a la conclusión o no de este problema.

Mi idea inicial fue que la implementación iOS de SQLite está, de hecho, descartando tus índices. La razón es porque inicialmente creas tu índice DB en el sistema x86/x64. El iOS es un procesador ARM, y los números se manejan de manera diferente. Si desea que sus índices sean rápidos, debe generarlos de tal manera que estén optimizados para el procesador en el que se buscarán.

Dado que SQLite es para múltiples plataformas, desde entonces se eliminarán los índices que se han creado en otra arquitectura y se reconstruirán. Sin embargo, dado que nadie quiere esperar a que se vuelva a generar un índice la primera vez que se accede, es muy probable que los desarrolladores de SQLite decidan abandonar el índice.

Después de profundizar en el código SQLite, he llegado a la conclusión de que esto es lo más probable pasando. Si no fuera por el motivo de la arquitectura del procesador, encontré el código (consulte analyze.c y otra metainformación en sqliteint.h) donde los índices se borraban si se generaban en un contexto inesperado. Mi corazonada es que el contexto que impulsa este proceso es cómo se construyó la estructura de datos b-tree subyacente para la clave existente. Si la instancia actual de SQLite no puede consumir la clave, la elimina.

Vale la pena mencionar que el simulador de iOS es solo eso, un simulador. No es un emulador del hardware. Como tal, su aplicación se ejecuta en un dispositivo pseudo-iOS, que se ejecuta en un procesador x86/x64.

Cuando su aplicación y SQLite DB se cargan en el dispositivo IOS, una variante de ARM-compilado se carga, que también conecta a las bibliotecas de ARM compilado dentro IOS. No pude encontrar el código específico de ARM asociado con SQLite, así que me imagino que Apple tuvo que modificarlo a su palo. El también podría ser parte del problema. Esto puede no ser un problema con el código raíz SQLite, podría ser un problema con la variante compilada Apple/ARM.

La única solución razonable que se me ocurre es que puede crear una aplicación de generador que ejecute en su máquina iOS. Ejecute la aplicación, cree las claves y, a continuación, extraiga el archivo SQLite del dispositivo. Me imagino que ese archivo funcionaría en todos los dispositivos, ya que todos los procesadores ARM utilizados por iOS son de 32 bits.

Una vez más, esta respuesta es un poco educada. Voy a volver a etiquetar tu pregunta como SQLite. Con suerte, un gurú puede encontrar esto y ser capaz de opinar sobre este tema. Realmente me gustaría saber la verdad para mi propio beneficio.

Cuestiones relacionadas