====== Lekkie technologie bazodanowe ====== ===== Wstęp i wymagania ===== Celem ćwiczenia jest zapoznanie się z technologią [[http://www.sqlite.org/|SQLite]] oraz identyfikację jej cech, ze szczególnym uwzględnieniem problemów wydajnościowych, jak i ilościowych (zajęcie pamięci). Do wykonania ćwiczenia niezbędna jest: * znajomość programowania w języku C, * umiejętność posługiwania się kompilatorem ''gcc'', * umiejętność posługiwania się systemem operacyjnym Linux. ===== Zaliczenie ===== Warunkiem zaliczenia zajęć jest implementacja poniższych programów oraz prezentacja rezultatów prowadzącemu. Możliwa jest prezentacja w późniejszym terminie, ale nie później niż na następnych zajęciach. ===== Ćwiczenie 1 ===== Napisz program w języku C, który: - dynamicznie zaallokuje pamięć pod następującą strukturę danych struct Rec { int id; /* unikalny identyfikator, klucz główny */ char name[20]; /* nazwa */ char desc[90]; /* opis */ }; - wypełni w/w strukturę przykładowymi danymi (dla uproszczenia dla każdego rekordu mogą być to takie same dane, z wyjątkiem pola będącego kluczem, tj. ''id''; dla kolejnych rekordów wartość ''id'' musi być równa zwiększonej o 1 wartości poprzedniej, ilość rekordów: 1000000), - przeszuka w/w strukturę w poszukiwaniu rekordu o ''id=999999''. Ponadto niezbędne jest aby program: - pomierzył czas wykonania wypełniania danymi, - pomierzył czas wykonania przeszukiwania, - pomierzył zużycie pamięci. **Uwaga!** Weź pod uwagę, że wszyscy pracują w laboratorium na tej samej maszynie, co może mieć wpływ na pomiary, szczególnie czasów wykonania. Każdy eksperyment powtórz kilka razy. ==== Wskazówki ==== Dynamiczna allokacja pamięci: data=malloc(sizeof(struct Rec)*MAX); if (data==NULL) { fprintf(stderr,"Mem alloc error\n"); return 1; } ... free(data); Pomiar czasu np. ''man 3 clock'': #include ... clock_t c1,c2; ... c1=clock(); ... c2=clock(); ... printf("time, c2-c1[s]: %f\n", ((float)c2-(float)c1)/CLOCKS_PER_SEC); Pomiar zużycia (maksymalnego) pamięci w Linuksie ''man 2 getrusage'': struct rusage mem; ... getrusage(RUSAGE_SELF,&mem); printf("Max mem usage[kB]: %ld\n", mem.ru_maxrss); ===== Ćwiczenie 2 ===== Powtórz ćwiczenie 1, ale używając SQLite. Wykonaj pomiary dla następujących różnych parametrów: - sposób przechowywania danych: pamięć vs. system plików, - rozmiar rekordu danych: np. (integer + 8 znaków + 15 znaków) vs. (integer + 18 znaków + 80 znaków) ==== Wskazówki ==== Aby "nawiązać połączenie z bazą danych" i poprawnie go zamknąć należy: int rc; sqlite3 *db; char loc[]=":memory:"; ... rc=sqlite3_open(loc,&db); if (rc){ fprintf(stderr, "Can't open database: %s\n", sqlite3_errmsg(db)); return 1; } ... sqlite3_close(db); Zmienna 'loc' przechowuje informacje o lokalizacji bazy danych, może to być nazwa pliku, albo wartość '':memory:'' (jak w w/w przykładzie), która powoduje umieszczenie bazy w pamięci. Do realizacji zapytań można użyć funkcji ''sqlite3_exec()'': rc=sqlite3_exec(db,"CREATE TABLE inv (id integer PRIMARY KEY, name varchar(20), desc varchar(90));", NULL, NULL, NULL); if (rc){ fprintf(stderr, "Database create table error: %s\n", sqlite3_errmsg(db)); return 1; } albo zapytań przygotowanych wcześniej: char *sql; sqlite3_stmt *stmt; ... sql="SELECT * FROM mojatabela"; rc=sqlite3_prepare_v2(db,sql,strlen(sql), &stmt, NULL); if (rc){ fprintf(stderr, "Database prepare statement error: %s\n", sqlite3_errmsg(db)); return 1; } do { rc=sqlite3_step(stmt); if (rc==SQLITE_ROW) { printf("I found sth: %s, %s, %d\n", (char *)sqlite3_column_text(stmt,0), (char *)sqlite3_column_text(stmt,2)); sqlite3_column_int(stmt,1), } } while (rc == SQLITE_ROW); sqlite3_finalize(stmt); Raz przygotowane zapytanie można wykonywać wiele razy, przydaje się ''sqlite3_reset()'': sql="INSERT INTO zakupy VALUES (?1,?2)"; rc=sqlite3_prepare_v2(db,sql,strlen(sql), &stmt, NULL); if (rc){ fprintf(stderr, "Database prepare statement error: %s\n", sqlite3_errmsg(db)); return 1; } for (i=0; i W przypadku wykonywania wielu operacji modyfikacji (np. insert) warto ująć je w transakcję. W przypadku SQLite ma to wpływ na sposób buforowania zapytań i danych, co znacznie przyspiesza ich wykonanie. sqlite3 *db; ... sqlite3_exec(db, "BEGIN TRANSACTION", NULL, NULL, NULL); ... sqlite3_exec(db, "COMMIT TRANSACTION", NULL, NULL, NULL ); ===== TBD ===== REDIS? FIXME