- P1. Czy PG ma możliwość buforowania/ogrzewania relacji?
- P2. Czy jest możliwy powrót do poprzedniego stanu pamięci podręcznej, w którym był pozostawiony przed wyłączeniem serwera bazy danych z powodu konserwacji?
We wcześniejszych wersjach PostgreSQL nie było szans na ocieplenie relacji lub przechowywanie stanów pamięci podręcznej, ale od PostgreSQL 9.4 wzwyż każde z powyższych zapytań (Q1,Q2) było adresowane za pomocą dwóch modułów contrib pg_prewarm i pg_hibernator . Pomimo tego, że wyróżniają się praktycznością, połączenie wydaje się być niezwykle opłacalne i przydatne w przyszłości dla DBA. W skrócie o wkładach:
pg_prewarm contrib (Autor:Robert Haas), zapewnia możliwość załadowania danych relacji do bufora bufora systemu operacyjnego lub bufora bufora PG. Posiada funkcję wstępnego lub ostatniego numeru bloku do wstępnego podgrzania. (Uwaga:nie ma specjalnej ochrony dla wstępnie ogrzanych danych przed wyrzuceniem pamięci podręcznej, a także, jeśli instancja bazy danych została zrestartowana, konieczne jest ponowne rozgrzanie relacji).
pg_hibernator contrib (autor:Gurjeet Singh), zapewnia możliwość automatycznego zapisywania listy zawartości współdzielonego bufora na dysku podczas zamykania bazy danych i automatycznie przywraca bufory podczas uruchamiania bazy danych, podobnie jak zapisywanie/przywracanie migawki shared_buffers. Wykorzystuje moduł PG 9.3 do rejestracji „procesu pracownika w tle” i odradza dwa procesy „Buffer Saver”, „Buffer Reader” do zapisywania/przywracania. Co ciekawe, przy odrobinie hacka pg_hibernator może również zezwolić w trybie gotowości slave na rozpoczęcie obsługi zapytań z pełną prędkością z taką samą zawartością mastera, zobaczy to za minutę :).
Na koniec potrzebujemypg_buffercache aby zajrzeć do aktualnej zawartości shared_buffers PostgreSQL. Ten moduł pomaga zrozumieć, jaki procent bufora zajmuje relacja.
Włączmy wszystkie te wkłady i zobaczmy, jak służą celom dwóch pytań (Q1,Q2). Zamierzam użyć tabeli „foo” o rozmiarze 885 MB na mojej lokalnej maszynie wirtualnej wraz ze standardowym zapytaniem pg_buffercache.
SELECT c.relname,
count(*) AS buffers
FROM pg_class c
INNER JOIN pg_buffercache b ON b.relfilenode=c.relfilenode AND c.relname='foo'
INNER JOIN pg_database d ON (b.reldatabase=d.oid AND d.datname=current_database())
GROUP BY c.relname
ORDER BY 2 DESC LIMIT 10;
Wykorzystanie pg_prewarm contrib i rozgrzewającej tabeli „foo”.
postgres=# create extension pg_prewarm;
CREATE EXTENSION
postgres=# dt+
List of relations
Schema | Name | Type | Owner | Size | Description
--------+------+-------+----------+--------+-------------
public | foo | table | postgres | 885 MB |
(1 row)
postgres=# select pg_prewarm('foo');
pg_prewarm
------------
113278
(1 row)
--pg_buffercache query output
relname | buffers
---------+---------
foo | 113278
(1 row)
Bardzo proste i proste użycie pg_prewarm z wyjściem bloków rozgrzanych w shared_buffers dla relacji „foo”. Z pg_buffercache wyjście zapytania możemy oszacować, że istnieje 113278 (113278 * 8 / 1024 =884MB ) buforów o rozmiarze bloku 8KB w relacji „foo”, która pasuje do wyjścia pg_prewarm. W tym przypadku, jeśli serwer Postgres zrestartuje się z jakiegoś powodu, shared_buffers są puste i DBA musi ponownie się rozgrzać, aby wrócić do poprzedniego ciepłego etapu. W przypadku pojedynczego stołu ponowne rozgrzanie jest zawsze proste, z wyjątkiem grupy stołów, która jest jej agonią.
W tym momencie możemy skorzystać z contrib pg_hibernator, ponieważ ma on elastyczność zapisywania zawartości shared_buffer i przywracania jej z powrotem podczas uruchamiania. Włączmy razem pg_hibernator/pg_prewarm i wykonajmy podobne ćwiczenie, po prostu włączając jeden krok ponownego uruchomienia i sprawdźmy, czy stan pamięci podręcznej powróci do stanu pierwotnego, czy nie. Nie będę omawiał instalacji pg_hibernator, ponieważ w git jest to bardzo dobrze opisane, jednak bezpośrednio przeskoczyłbym do części implementacyjnej i uruchomił serwer z pg_hibernatorem.
postgres 24623 1 0 02:06 pts/4 00:00:00 /usr/local/pgpatch/pg/bin/postgres -D /usr/local/pgpatch/pg/data_10407
postgres 24627 24623 0 02:06 ? 00:00:00 postgres: logger process
postgres 24631 24623 0 02:06 ? 00:00:00 postgres: checkpointer process
postgres 24632 24623 0 02:06 ? 00:00:00 postgres: writer process
postgres 24633 24623 0 02:06 ? 00:00:00 postgres: wal writer process
postgres 24634 24623 0 02:06 ? 00:00:00 postgres: autovacuum launcher process
postgres 24635 24623 0 02:06 ? 00:00:00 postgres: archiver process
postgres 24636 24623 0 02:06 ? 00:00:00 postgres: stats collector process
postgres 24637 24623 0 02:06 ? 00:00:00 postgres: bgworker: Buffer Saver
postgres 24638 24623 11 02:06 ? 00:00:01 postgres: bgworker: Block Reader 2
In database server logs at startup time:
-bash-4.1$ more postgresql-2014-06-02_083033.log
LOG: database system was shut down at 2014-06-02 08:13:00 PDT
LOG: starting background worker process "Buffer Saver"
LOG: database system is ready to accept connections
LOG: autovacuum launcher started
Ponieważ po raz pierwszy pg_hibernator jest w grze, możesz zobaczyć dwa procesy, a także logi z informacjami dotyczącymi uruchomienia „Oszczędzania bufora”. Teraz podgrzejmy wstępnie relację „foo” i zrestartuj serwer, później sprawdź stan bufora, czy pg_hibernator wypełnił bufor z powrotem tam, gdzie został.
-bash-4.1$ psql -p 10407
psql (9.4beta1)
Type "help" for help.
postgres=# select pg_prewarm('foo');
pg_prewarm
------------
113278
(1 row)
--pg_buffercache query output
relname | buffers
---------+---------
foo | 113278
(1 row)
postgres=# q
-bash-4.1$ /usr/local/pgpatch/pg/bin/pg_ctl -D /usr/local/pgpatch/pg/data_10407 stop
waiting for server to shut down.... done
server stopped
-bash-4.1$ ls -l $PGDATA/pg_hibernator/
total 12
-rw------- 1 postgres postgres 160 Jun 3 01:41 1.global.save
-rw------- 1 postgres postgres 915 Jun 3 01:41 2.postgres.save
-bash-4.1$ /usr/local/pgpatch/pg/bin/pg_ctl -D /usr/local/pgpatch/pg/data_10407 start
server starting
Zrestartowaliśmy serwer bazy danych, sprawdźmy logi
-bash-4.1$ more postgresql-2014-06-03_020601.log
LOG: database system was shut down at 2014-06-03 02:05:57 PDT
LOG: starting background worker process "Buffer Saver"
LOG: database system is ready to accept connections
LOG: autovacuum launcher started
LOG: registering background worker "Block Reader 2"
LOG: starting background worker process "Block Reader 2"
LOG: Block Reader 2: restored 113433 blocks
LOG: Block Reader 2: all blocks read successfully
LOG: worker process: Block Reader 2 (PID 24638) exited with exit code 1
LOG: unregistering background worker "Block Reader 2"
LOG: registering background worker "Block Reader 1"
LOG: starting background worker process "Block Reader 1"
LOG: Block Reader 1: restored 20 blocks
LOG: Block Reader 1: all blocks read successfully
LOG: worker process: Block Reader 1 (PID 24664) exited with exit code 1
LOG: unregistering background worker "Block Reader 1"
Tak więc „Czytnik bufora” przywrócił bloki 113433 + 20, z których 113278 należy do relacji „foo”. Świetnie, połączmy się i zobaczmy.
-bash-4.1$ psql -p 10407
psql (9.4beta1)
Type "help" for help.
--pg_buffercache query output
relname | buffers
---------+---------
foo | 113278
(1 row)
Fajnie… pg_hibernator przywrócił stan rozgrzania pamięci podręcznej bez ingerencji DBA.
Kolejną zaletą pg_hibernator jest to, że nowo utworzona rezerwa może mieć taką samą zawartość współdzielonego bufora jak master, dzięki czemu rezerwa może zacząć obsługiwać zapytania z pełną prędkością. Aby wykonać to ćwiczenie, wykonując kopię zapasową katalogu $PGDATA, przekazałem SIGTERM proces „Buffer Saver”, aby zapisywał zawartość shared_buffers w bieżącym stanie na dysk (katalog $PGDATA/pg_hibernator), a następnie kontynuowałem konfigurację trybu gotowości.
postgres 24637 24623 0 02:06 ? 00:00:00 postgres: bgworker: Buffer Saver
postgres 24653 15179 0 02:06 ? 00:00:01 postgres: wal receiver process streaming 1/6A000A10
postgres 24654 24623 0 02:06 ? 00:00:00 postgres: wal sender process postgres ::1(65011) streaming 1/6A000A10
Po konfiguracji mój slave zaczął z tą samą zawartością podstawowego
-bash-4.1$ psql -p 10477
psql (9.4beta1)
Type "help" for help.
postgres=# select pg_is_in_recovery();
pg_is_in_recovery
-------------------
t
(1 row)
--pg_buffercache query output
relname | buffers
---------+---------
foo | 113278
(1 row)
Dziękuję obu autorom za wspaniałe rozszerzenie dotyczące buforowania.