Archiwum

Archiwum dla ‘Bazy danych’ Kategoria

Logowanie do pliku wszystkich zapytań w PostgreSQL

Lipiec 21st, 2009 zergu 1 komentarz

Aby zapisywać w logach wszystkie zapytania jakie się wykonują w naszej bazie danych wystarczy zmienić tylko kilka opcji konfiguracyjnych i zrestartować serwer Postgresa. Tak więc w pliku postgresql.conf (znajdującym się katalogu, w którym trzymane są bazy, zwykle tam gdzie wskzuje zmienna PGDATA, a w „debianopodobnych” w /etc/postgresql/8.x/main/) w sekcji Error Reporting and Logging ustawiamy:


log_destination = 'stderr'
logging_collector = on
log_directory = 'pg_log'
log_filename = 'postgresql-%Y-%m-%d_%H%M%S.log'
log_statement = 'all'

Gdzie:

  • log_destination — tak jest ustawiony domyślnie, więc tylko sprawdzamy czy się nic nie zmieniło.
  • logging_collector — włączamy, aby logi trafiające do stderr mogły być zapisane do pliku.
  • log_directory — dowolna nazwa katalogu dla logów, znajdującego się katalogu gdzie trzymamy bazę. Jeśli nie ma takiego należy go utworzyć i upewnić się, że postgres ma uprawnienia do zapisu.
  • log_filename — nazwa pliku z logami, tutaj ze zdefiniowanym dniem.
  • log_statement — grupa zapytań jakie chcemy zapisywać do pliku. Tutaj wybrane wszystkie. Inne opcje: none (wyłączenie zapisu), ddl (zapytania CREATE, ALTER, DROP), mod (tutaj zapewne chodzi o zapytania UPDATE, ale tego nie testowaliśmy).
Kategorie:Bazy danych Tagi:

Zapamiętywanie hasła dla PostgreSQL w pliku

Czerwiec 30th, 2009 zergu 1 komentarz

Jeśli często korzystamy z psql, gdzie wymagana jest autoryzacja, lub też chcemy umożliwić wykonanie jakiegoś skryptu automatycznie (np. z crona) można zapamiętać sobie hasło w specjalnym pliku ~/.pgpass. Składnia jest następująca:


host:port:baza:użytkownik:hasło
host2:port2:baza2:użytkownik2:hasło2
…

Przy czym można użyć *, jeśli nie chcemy jednoznacznie identyfikować któregoś parametru (oprócz hasła oczywiście). Należy pamiętać, że podczas łączenia może być koniecznie podanie hosta i użytkownika, ponieważ ten mechanizm służy jedynie do dopasowywania samych haseł. Na koniec bardziej życiowy przykład dla tych, co tylko kod czytają:


*:*:produkty:admin:tajnehaslo

UPDATE: Tak jak Mistrz Wu w komentarzu sugeruje, nie tyle warto co trzeba zmienić uprawnienia dla tego pliku na 0600. Po dodaniu uprawnień dla grup i innych plik byłby ignorowany.

Kategorie:Bazy danych Tagi:

PostgreSQL: Czas życia krotki

Styczeń 14th, 2009 zergu Brak komentarzy

Jak sprawdzić ile godzin minęło od utworzenia rekordu (lub po prostu obliczyć odstęp czasu od dowolnego momentu)?

SELECT extract (epoch from (now() - created_at))/3600 AS lifetime FROM …

Zakładając oczywiście, że w kolumnie created_at trzymamy dokładny czas utworzenia rekordu. Funkcja extract może operować na timestampach (znacznikach czasu) i interwałach (wynik działania now() - created_at jest właśnie interwałem). Powyższe zapytanie oblicza godziny. Poza tym możemy „extractować” wiele innych wartości, takich jak:

  • century
  • day
  • decade
  • dow (dzień tygodnia)
  • epoch
  • hour
  • isodow
  • isoyear
  • microseconds
  • millennium
  • milliseconds
  • minute
  • month
  • quarter
  • second
  • timezone
  • timezone_hour
  • timezone_minute
  • week
  • year

Szczegóły w dokumentacji.

Aktualizacja oprogramowania a wydajność

Grudzień 19th, 2008 zergu 2 comments

Krótkie spostrzeżenie na temat wydajności aplikacji (praktycznie nie wypełnionej danymi) po następujących aktualizacjach:

  • Symfony 1.1 → Symfony 1.2
  • Propel 1.2 → Propel 1.3
  • PHP 5.2.0 → PHP 5.2.6
  • PostgreSQL 8.1 → PostgreSQL 8.3

Prosty test został wykonany za pomocą ApacheBenchmarka:
ab -c 5 -n 300 -H 'Connection: close'. Jak widać symulacja bazowała na 300 użytkownikach, przy czym do 5 na raz wchodziło na stronę.

Przed

Próba 1:

Requests per second:    4.34 [#/sec] (mean)
Time per request:       1152.770 [ms] (mean)
Time per request:       230.554 [ms] (mean, across all concurrent requests)

Próba 2:

Requests per second:    4.79 [#/sec] (mean)
Time per request:       1043.581 [ms] (mean)
Time per request:       208.716 [ms] (mean, across all concurrent requests)

Po

Próba 1:

Requests per second:    5.33 [#/sec] (mean)
Time per request:       937.318 [ms] (mean)
Time per request:       187.464 [ms] (mean, across all concurrent requests)

Próba 2:

Requests per second:    5.39 [#/sec] (mean)
Time per request:       927.979 [ms] (mean)
Time per request:       185.596 [ms] (mean, across all concurrent requests)

Oczywiście z uwagi na brak testów pomiędzy poszczególnymi zmianiami ciężko jest powiedzieć coś więcej, niż tyle że warto aktualizować, choćby o tego jednego requesta na sekundę ;). Ot, taka ciekawostka.

Migracja MySQL → PostgreSQL (aplikacji w Symfony 1.1)

Grudzień 15th, 2008 zergu Brak komentarzy

Jako, że w MySQL nie dzieje się najlepiej postanowiliśmy zmigrować jeden z naszych projektów na Postgresa. Bazuje on na Symfony 1.1, co oznacza starego Propela 1.2.

Samo przestawienie połączenia nie stwarzało większych problemów, wystarczyło w databases.yml i propel.ini przerobić mysql na pgsql:


all:
  propel:
    class:          sfPropelDatabase
    param:
    persistent:       true
    phptype:          pgsql
…

propel.database            = pgsql
propel.database.createUrl  = pgsql://zdzislaw@localhost/
propel.database.url        = pgsql://zdzislaw@localhost/piwo

Oczywiście o przebudowaniu modelu, utworzeniu bazy i jej struktury nie ma co wspominać :)

Brak SERIAL

Niestety, propelowy generator skyptu SQL tworzącego schemat bazy nie uraczył nas typem SERIAL przy identyfikatorach, jednakże w samej aplikacji nie stwarza to problemów, ponieważ sekwencje i tak są wykorzystywne. Trzeba po prostu uważać podczas zabawy w CLI Postgresa.

Dane binarne pobierane „ręcznie”

Drobny problem pojawił się, w miejscach, gdzie pobieraliśmy dane binarne (BLOB/BYTEA) własnym zapytaniem, omijając nieco wbudowany we framework Active Record. Dane takie trzeba było dodatkowo „od-eskejpować” funkcją stripcslashes.

AKTUALIZACJA:
Po aktualizacji projektu do Symfony 1.2 (chociaż tutaj właściwie chodzi o aktualizację Propela do wersji 1.3) okazało się, że żadne stripcslashes nie będzie potrzebne, ponieważ dane binarne (blob) są pobierane jako… stream. Innymi słowy, przy zapytaniach tworzonych samodzielnie, do akcji musiała wkroczyć funkcja stream_get_contents.

GROUP BY

Najwięcej problemów sprawiło grupowanie, ponieważ Postgres ostro trzyma się standardów i wymusza podanie wszystkich kolumn, które są używane, w klauzuli GROUP BY. MySQL natomiast „po cichu” sam sobie je dołączał. Różnicę w zachowaniu prezentuje poniższy (nie mający większego sensu) przykład:

psql=# select sum(id), id from users;
ERROR: column "users.id" must appear in the GROUP BY clause or be used in an aggregate function

mysql> select sum(id), id from users;
+---------+----+
| sum(id) | id |
+---------+----+
| 45      | 1  |
+---------+----+

CONCAT

Łącznie ciągów znaków odbywa się całkiem inaczej w MySQL i PgSQL. W tym pierwszym wykonuje się to za pomocą funkcji CONCAT(), natomiast w drugim wykorzystuje się operator ||.

FORMAT

W naszym kodzie używaliśmy MySQL-owego FORMAT(), aby ograniczyć liczbę cyfr po przecinku, którego w Postgresie nie ma. Można by tutaj użyć funkcji ROUND(), obecnej i tu i tu, jednak zdecydowaliśmy się przenieść rozwiązanie tego problemu do warstwy języka programowania.

LIKE

LIKE w Postgresie, w przeciwieństwie do MySQL, rozróżnia wielkość znaków. Jednak naprawa tej przypadłości sprowadza się jedynie od zamiany LIKE na ILIKE.

?

Na koniec coś, czego do końca nie udało nam się zrozumieć. Zaraz po przestawieniu konfiguracji na pgsql, strona przestała odpowiadać (a dokładniej odpowiadała gdy połączenie z bazą przekroczyło czas oczekiwania). Niemniej jednak Postgres sam w sobie działał, jak również możliwe było połączenie z „czystego” PHP. Jedynie aplikacja Symfony dziwnie się zachowywała. W logach pojawiało się następujące stwierdzenie:
Dec 4 11:20:22 myhost httpd: [wrapped: Could not connect [Native Error: pg_pconnect() [function.pg-pconnect]: Unable to connect to PostgreSQL server: server closed the connection unexpectedly This probably means the server terminated abnormally before or while processing the request.] [User Info: host=localhost port=80 dbname='piwo' user='zdzislaw'] [User Info: Array]]

Taka sama sytuacja powtórzyła się na dwóch różnych maszynach (jednak na tym samym systemie operacyjnym). Niezrozumiałe jest to, że w czasie różnych zabaw z konfiguracją wszystko wracało do normy, jednak nie bardzo da się znaleźć konkretną przyczynę. Innymi słowy nie potrafimy zreprodukować tej sytuacji.

Krótko podsumowując — migracja rozwiniętego projektu w Symfony nie jest bardzo trudna, chociaż zawsze mogłaby być mniej problematyczna, gdyby unikać rozwiązań specyficznych dla danego systemu baz danych. Ale czy warto?