2012-05-04 18 views
6

Wniosek
Problem chyba zamknięty.
Wygląda na to, że problem nie miał nic wspólnego z metodologią, ale że XCode nie wyczyścił poprawnie projektu pomiędzy wersjami. Wygląda na to, że po tych wszystkich testach plik sqlite, który był używany był wciąż pierwszym, który nie był indeksowany ......
Uważaj na XCode 4.3.2, mam tylko problemy z Clean nie czyszczenia lub dodawanie plików do projektu nie są automatycznie dodawane do środków wiązek ...
Dzięki dla różnych odpowiedzi ..Jaki jest najszybszy sposób na załadowanie dużego pliku CSV do danych podstawowych

Update 3
Odkąd zaprosić kogoś po prostu wypróbować te same kroki, aby zobaczyć jeśli uzyskają takie same wyniki, pozwól mi szczegółowo opisać, co zrobiłem:
Zacznę od pustego projektu
ja zdefiniował datamodel z jednego podmiotu, 3 atrybutów (2 ciągi, 1 float)
Pierwszy ciąg jest indeksowana
enter image description here

W nie finishLaunchingWithOptions wzywam:

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

Kod dla populateDb jest poniżej:

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

Cała reszta to domyślny kod danych podstawowych, nic nie dodano.
Uruchomiłem to w symulatorze.
idę do ~/Library/Application Support/iPhone Simulator/5.1/Aplikacje // Documents
znajduje się plik SQLite, który jest generowany

Biorę to i skopiować go w moim pakiecie

I komentarz na wezwanie do populateDb

edytować persistentStoreCoordinator skopiować pliku sqlite od wiązki do dokumentów przy pierwszym uruchomieniu

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


Usuwam aplikację z symulatora, sprawdzam, czy ~/Library/Application Support/iPhone Simulator/5.1/Applications/jest teraz usunięty
Przebudowuję i ponownie uruchamiam
Zgodnie z oczekiwaniami plik sqlite jest kopiowany do ~/Library/Application Support/iPhone Simulator/5.1/Applications // Documents

Jednak rozmiar pliku jest mniejszy niż w pakiecie, znacznie! Wykonuje również proste zapytanie z predykatem takim jak ten predykat = [NSPredicate predicateWithFormat: @ "string1 ==% @", string1]; wyraźnie pokazuje, że łańcuch1 nie jest indeksowana już

obserwuję, że tworzę nową wersję datamodel, z bezsensownego aktualizacji, tak aby wymusić lekką migrację
Jeśli uruchamianie na symulatorze, migracja trwa kilka sekundy, baza danych podwaja się, a to samo zapytanie zajmuje teraz mniej niż sekundę, aby powrócić zamiast minut.
Rozwiązałoby to mój problem, wymuszanie migracji, ale ta sama migracja trwa 3 minuty na iPadzie i dzieje się na pierwszym planie.
W tej chwili jestem na miejscu, najlepszym rozwiązaniem dla mnie będzie nadal zapobieganie usuwaniu indeksów, każde inne rozwiązanie importowania w momencie uruchomienia zajmuje po prostu zbyt dużo czasu.
Daj mi znać, jeśli potrzebujesz więcej wyjaśnień ...

Aktualizacja 2
Więc najlepszym wynikiem miałem tak daleko jest do zainicjowania bazy danych rdzeń z pliku sqlite wytwarzanej z szybkiego narzędzia z podobnym model danych, ale bez indeksów ustawionych podczas tworzenia pliku sqlite. Następnie importuję ten plik sqlite do głównej aplikacji danych z ustawionymi zestawami indeksów i zezwalam na niewielką migrację. Za 2 miliony nagrań na nowym iPadzie, ta migracja trwa 3 minuty. Ostateczna aplikacja powinna mieć 5-krotność tej liczby rekordów, więc wciąż szukamy długiego czasu przetwarzania. Jeśli pójdę tą drogą, nowe pytanie brzmi: czy lekka migracja może być przeprowadzona w tle?

Aktualizacja
moje pytanie nie jest, jak stworzyć narzędzie do wypełnienia bazy danych Core, a następnie zaimportować plik sqlite w mojej aplikacji.
Wiem, jak to zrobić, zrobiłem to niezliczoną ilość razy.
Ale do tej pory nie zdawałem sobie sprawy, że taka metoda może mieć pewne skutki uboczne: w moim przypadku indeksowany atrybut w wynikowej bazie danych wyraźnie "cofnął indeks" podczas importowania pliku sqlite w ten sposób.
Jeśli byłeś w stanie zweryfikować, czy jakiekolwiek indeksowane dane są nadal indeksowane po takim przeniesieniu, chciałbym wiedzieć, jak postępować, lub w inny sposób, jaka byłaby najlepsza strategia efektywnego rozmieszczenia takiej bazy danych.

Original

Mam dużego pliku CSV (mln linii) z 4 kolumn, smyczki i pływaków. To jest dla aplikacji na iOS.
Potrzebuję tego, aby załadować do podstawowych danych przy pierwszym załadowaniu aplikacji.
Aplikacja jest praktycznie nieczynna, dopóki dane nie będą dostępne, więc czas ładowania ma znaczenie, ponieważ użytkownik po raz pierwszy oczywiście nie chce, aby aplikacja ładowała się 20 minut, zanim będzie mogła go uruchomić.

W tej chwili mój obecny kod zajmuje 20 minut na nowym iPadzie, aby przetworzyć plik csv o wartości 2 milionów wierszy.

Używam kontekst tła aby nie zablokować UI i zapisać kontekście każde 1000 rekordów

Pierwszym pomysłem było to, aby wygenerować bazę danych na symulatorze, a następnie skopiować/wkleić go w folderze dokumentu na pierwsze uruchomienie, ponieważ jest to powszechny nieoficjalny sposób zasiania dużej bazy danych. Niestety, wydaje się, że indeksy nie przetrwały takiego transferu i chociaż baza danych była dostępna już po kilku sekundach, wydajność jest straszna, ponieważ moje indeksy zostały utracone. Już napisałem pytanie o indeksy, ale nie wydaje się, aby była to dobra odpowiedź.

Więc co szukam, albo:

  • sposób, aby poprawić wydajność przy załadunku miliony rekordów w danych podstawowych
  • jeśli baza danych jest wstępnie załadowany i przeniósł się na pierwszym uruchomieniu drogę zachować moje indeksy
  • najlepsze praktyki dotyczące obsługi tego rodzaju scenariusza.Nie pamiętam, aby użyć jakiejkolwiek aplikacji, która wymaga ode mnie czekania na x minut przed pierwszym użyciem (ale może na Daily, a to było straszne doświadczenie).
  • Dowolny twórczy sposób, aby sprawić, by użytkownik poczekał, nie zdając sobie z tego sprawy: import w tle podczas przechodzenia przez samouczek, itd ...
  • Nie używając danych podstawowych?
  • ...
+0

W jaki sposób skończyłeś "oczyścić" projekt, aby działał poprawnie? – lnafziger

+0

Clean nie działa, ale ponowne uruchomienie laptopa, ręczne czyszczenie wszystkich odniesień do pliku itp., Wydaje się, że "rozwiązał" problem. Dziwne ... chociaż musiałem też usunąć lekkie linie migracji, aby wymusić migrację (ponieważ zajęłoby to wiele minut). Ogólnie rzecz biorąc, nie jest to czysta implementacja, na którą mam nadzieję, ale działa ... dopóki wersja 2 nie wymaga aktualizacji modelu danych, wtedy mam kłopoty. –

Odpowiedz

6

Wstępnie wygenerować bazę danych przy użyciu aplikacji w trybie offline (powiedzmy, narzędzie wiersza polecenia) napisane w kakao, które działa na OS X, a korzystające z tej samej ramy DANE PODSTAWOWE że iOS zastosowania . Nie musisz się martwić o "przetrwanie indeksów" lub cokolwiek - wyjście to plik bazy danych .sqlite, generowany przez rdzeń danych, bezpośrednio i natychmiast wykorzystywany przez aplikację iOS.

Tak długo, jak można zrobić pokolenie DB off-line, jest to najlepsze rozwiązanie zdecydowanie. Z powodzeniem wykorzystałem tę technikę do wstępnie wygenerowanych baz danych dla wdrożenia systemu iOS. Sprawdź moje poprzednie pytania/odpowiedzi, aby uzyskać więcej szczegółów.

+0

Ja też zrobiłem to samo, bez problemów z indeksem ... – lnafziger

+0

Co masz na myśli, nie muszę się martwić o moje indeksy; jak powiedziałem w moim pytaniu, zrobiłem tę dokładną metodę, wynik był plikiem bazy danych sqlite (200Mb), a gdy został użyty w mojej aplikacji z tym samym modelem, plik zszedł do 110Mb, a wyraźna wydajność sugerowała, że ​​moje indeksy nie były pracujący. Więc martwię się o moje indeksy, to jest cały sens! –

+0

@nafziger, czy masz na myśli indeksy w swoim podstawowym modelu danych i czy wiesz, że te indeksy nadal działają tak, jak powinny, gdy ponownie wykorzystasz ten plik sqlite? Jeśli tak, to jaka była Twoja metodologia, aby upewnić się, że indeksy nadal działają? –

0

Właśnie zaczynam od SQLite i potrzebuję zintegrować DB z jedną z moich aplikacji, która będzie zawierała wiele indeksowanych danych w bazie danych SQLite. Miałem nadzieję, że mogę zrobić jakąś metodę, w której zbiorczo wstawiłbym moje informacje do pliku SQLite i dodałbym ten plik do mojego projektu. Po odkryciu i przeczytaniu twojego pytania, dostarczonej odpowiedzi i licznych komentarzy, zdecydowałem się sprawdzić źródło SQLite, aby zobaczyć, czy uda mi się zdobyć głowy lub ogony tego problemu.

Moja początkowa myśl polegała na tym, że implementacja SQLite w systemie iOS w rzeczywistości powoduje odrzucenie indeksów. Powodem jest to, że początkowo tworzysz indeks DB w systemie x86/x64. System iOS jest procesorem ARM, a liczby są obsługiwane inaczej. Jeśli chcesz, aby indeksy były szybkie, generuj je w taki sposób, aby były zoptymalizowane dla procesora, w którym będą wyszukiwane.

Ponieważ SQLite jest przeznaczone dla wielu platform, od tego czasu wszystkie indeksy, które zostały utworzone w innej architekturze, zostaną odbudowane i odbudowane. Jednakże, ponieważ nikt nie chce czekać na odbudowę indeksu po raz pierwszy, jest on dostępny, dlatego deweloperzy SQLite najprawdopodobniej zdecydowali się po prostu upuścić indeks.

Po wykopaniu kodu SQLite, doszedłem do wniosku, że jest to najprawdopodobniej dzieje się. Jeśli nie ze względu na architekturę procesora, znalazłem kod (zob. analyze.c i inne meta-informacje w sqliteint.h), gdzie indeksy zostały usunięte, jeśli zostały wygenerowane w nieoczekiwanym kontekście. Moje przeczucie polega na tym, że kontekstem, który napędza ten proces, jest budowa bazowej struktury drzewa B dla istniejącego klucza. Jeśli bieżąca instancja SQLite nie może pobrać klucza, usuwa go.

Warto wspomnieć, że symulator iOS to po prostu ... symulator. To nie jest emulator tego sprzętu. W związku z tym Twoja aplikacja działa na urządzeniu pseudo-iOS działającym na procesorze x86/x64.

Po załadowaniu aplikacji i bazy danych SQLite do urządzenia z systemem iOS, zostanie załadowany wariant z kompilacją ARM, który również łączy się z bibliotekami skompilowanymi z ARM w systemie iOS. Nie mogłem znaleźć specyficznego dla ARM kodu związanego z SQLite, więc wyobrażam sobie, że Apple musiał go zmodyfikować na swój garnitur. Może to być również częścią problemu. Może to nie być problem z kodem root-SQLite, może to być problem z wersją skompilowaną przez Apple/ARM.

Jedynym rozsądnym rozwiązaniem, które mogę wymyślić, jest to, że można utworzyć aplikację generatora uruchamianą na komputerze z systemem iOS. Uruchom aplikację, utwórz klucze, a następnie zgraj plik SQLite z urządzenia. Wyobrażam sobie, że taki plik działa na wszystkich urządzeniach, ponieważ wszystkie procesory ARM używane przez iOS są 32-bitowe.

Ponownie, ta odpowiedź jest nieco wykształcona. Zamierzam ponownie oznaczyć twoje pytanie jako SQLite. Mam nadzieję, że guru może to znaleźć i być w stanie rozważyć tę kwestię. Naprawdę chciałbym poznać prawdę dla mojej własnej korzyści.

Powiązane problemy