Jak mogę zapewnić, że zmaterializowany widok jest zawsze aktualny?

Będę musiał wywoływać REFRESH MATERIALIZED VIEW przy każdej zmianie tabel, prawda? Dziwi mnie, że w sieci nie ma zbyt wielu dyskusji na ten temat.

Jak mam to zrobić?

Myślę, że najlepsza połowa odpowiedzi tutaj jest to, czego szukam: https://stackoverflow.com/a/23963969/168143

Czy jest w tym jakieś niebezpieczeństwo? Jeśli aktualizacja widoku nie powiedzie się, transakcja na wywołanie aktualizacji, wstawić, itp. zostać wycofanym? (tego właśnie chcę... I think)
Author: Community, 2015-04-03

2 answers

Będę musiał powoływać się na każdą zmianę tabel, prawda?

Tak, PostgreSQL sam w sobie nigdy nie wywoła go automatycznie, musisz to zrobić w jakiś sposób.

Jak mam to zrobić?
Można to osiągnąć na wiele sposobów. Przed podaniem kilku przykładów należy pamiętać, że REFRESH MATERIALIZED VIEW polecenie blokuje widok w trybie AccessExclusive, więc podczas działania nie można nawet wykonać SELECT na stolik.

Chociaż, jeśli jesteś w wersji 9.4 lub nowszej, możesz dać mu opcję CONCURRENTLY:

REFRESH MATERIALIZED VIEW CONCURRENTLY my_mv;

To uzyska wyłączność i nie zablokuje SELECT zapytań, ale może mieć większy narzut (zależy od ilości zmienionych danych, jeśli kilka wierszy się zmieni, to może być szybciej). Chociaż nadal nie można uruchomić dwóch komend REFRESH jednocześnie.

Odśwież ręcznie

Jest to opcja do rozważenia. Szczególnie w przypadku ładowania danych lub partii aktualizacje (np. system, który ładuje tony informacji / danych tylko po długim okresie czasu) często na końcu są operacje modyfikujące lub przetwarzające dane, więc można łatwo dołączyć operację REFRESH na końcu.

Planowanie operacji odświeżania

Pierwszą i powszechnie używaną opcją jest użycie jakiegoś systemu planowania do wywołania odświeżania, na przykład można skonfigurować podobny w zadaniu cron:

*/30 * * * * psql -d your_database -c "REFRESH MATERIALIZED VIEW CONCURRENTLY my_mv"

I wtedy twój zmaterializowany widok będzie odświeżany co 30 minut.

Rozważania

Ta opcja jest naprawdę dobra, szczególnie z opcją CONCURRENTLY, ale tylko wtedy, gdy możesz zaakceptować dane, które nie są w 100% aktualne przez cały czas. Należy pamiętać, że nawet z CONCURRENTLY lub bez REFRESH, Komenda REFRESH musi uruchomić całe zapytanie, więc musisz poświęcić czas potrzebny na uruchomienie wewnętrznego zapytania przed rozważeniem czasu na zaplanowanie REFRESH.

Odświeżanie za pomocą spustu

Inną opcją jest wywołanie REFRESH MATERIALIZED VIEW w funkcji wyzwalającej, jak to:

CREATE OR REPLACE FUNCTION tg_refresh_my_mv()
RETURNS trigger LANGUAGE plpgsql AS $$
BEGIN
    REFRESH MATERIALIZED VIEW CONCURRENTLY my_mv;
    RETURN NULL;
END;
$$;

Następnie, w dowolnej tabeli, która obejmuje zmiany w widoku, robisz:

CREATE TRIGGER tg_refresh_my_mv AFTER INSERT OR UPDATE OR DELETE
ON table_name
FOR EACH STATEMENT EXECUTE PROCEDURE tg_refresh_my_mv();

Rozważania

Ma kilka krytycznych pułapek wydajności i współbieżności:]}
    W związku z tym, że nie jest to możliwe, nie jest to konieczne, ponieważ nie jest to konieczne.]}
  1. nawet z CONCURRENTLY, jedna REFRESH nadal blokuje inną, więc każda wstawka/aktualizacja/Usuń na zaangażowanych tabelach będzie bądź serializowana.
Jedyną sytuacją, którą mogę uznać za dobry pomysł, jest to, że zmiany są naprawdę rzadkie.

Odświeżanie za pomocą LISTEN / NOTIFY

Problem z poprzednią opcją polega na tym, że jest synchroniczna i nakłada duży narzut na każdą operację. Aby to złagodzić, można użyć wyzwalacza jak wcześniej, ale to wywołuje tylko NOTIFY operacja:

CREATE OR REPLACE FUNCTION tg_refresh_my_mv()
RETURNS trigger LANGUAGE plpgsql AS $$
BEGIN
    NOTIFY refresh_mv, 'my_mv';
    RETURN NULL;
END;
$$;

Więc możesz zbudować aplikację, która utrzymuje połączenie i używa LISTEN operacja w celu identyfikacji potrzeby wywołania REFRESH. Jednym z fajnych projektów, które możesz użyć do przetestowania tego jest pgsidekick , w tym projekcie możesz użyć skryptu powłoki do zrobienia LISTEN, więc możesz zaplanować REFRESH jako:

pglisten --listen=refresh_mv --print0 | xargs -0 -n1 -I? psql -d your_database -c "REFRESH MATERIALIZED VIEW CONCURRENTLY ?;"

Lub użyj pglater (również wewnątrz pgsidekick), aby upewnić się, że nie dzwonisz zbyt często. Na przykład, możesz użyć następującego wyzwalacza, aby zrobić to REFRESH, ale w ciągu 1 minuty (60 sekund):

CREATE OR REPLACE FUNCTION tg_refresh_my_mv()
RETURNS trigger LANGUAGE plpgsql AS $$
BEGIN
    NOTIFY refresh_mv, '60 REFRESH MATERIALIZED VIEW CONCURRENLTY my_mv';
    RETURN NULL;
END;
$$;

Więc nie zadzwoni REFRESH w mniej niż 60 sekund od siebie, a także jeśli NOTIFY wiele razy w czasie krótszym niż 60 sekund, REFRESH zostanie uruchomiony tylko raz.

Rozważania

Jako opcja cron, ta jest również dobra tylko wtedy, gdy możesz wydobyć trochę starych danych, ale ma to tę zaletę, że {[12] } jest wywoływany tylko wtedy, gdy jest naprawdę potrzebny, więc masz mniej kosztów ogólnych, a także dane są aktualizowane bardziej bliżej, gdy są potrzebne.

OBS: nie próbowałem jeszcze kodów i przykładów, więc jeśli ktoś znajdzie błąd, literówkę lub próbuje i działa (lub nie), proszę dać mi znać.

 48
Author: MatheusOl,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-01-17 08:23:56

Pozwól, że zwrócę uwagę na trzy rzeczy z poprzedniej odpowiedzi Matheusola-technologia pglater.

  1. Jako ostatni element tablicy long_options powinien zawierać "{0, 0, 0, 0}" element wskazywany na https://linux.die.net/man/3/getopt_long przez wyrażenie " ostatni element tablicy musi być wypełniony zerami."Więc powinno być -

    static struct option long_options[] =     {
          //......
          {"help", no_argument, NULL, '?'},
          {0, 0, 0, 0} 
    };
    
  2. On the malloc / free thing -- one free (for char listen = malloc(...);) brakuje. W każdym razie malloc spowodował awarię procesu pglater na CentOS (ale nie na Ubuntu - Nie wiem dlaczego). Dlatego zalecam użycie tablicy znaków i przypisanie nazwy tablicy do wskaźnika znaków (zarówno char, jak i char**). Podczas wykonywania tego zadania musisz wymusić konwersję typu(przypisanie wskaźnika).

    char block4[100];
    ...
    password_prompt = block4;
    ...
    char block1[500];
    const char **keywords = (const char **)&block1;
    ...
    char block3[300];
    char *listen = block3;
    sprintf(listen, "listen %s", id);
    PQfreemem(id);
    res = PQexec(db, listen);
    
  3. Użyj poniższej tabeli, aby obliczyć timeout, gdzie MD to mature_duration, która jest różnicą czasu między ostatnim punktem odświeżania (LR) i aktualny czas.

    When MD >= callback_delay(cd) = = > timeout: 0

    When MD + PING_INTERVAL > = cd = = > timeout: cd-MD [=cd - (now-LR)]

    Gdy MD + PING_INTERVAL timeout: PI

Aby zaimplementować ten algorytm (trzeci punkt), powinieneś init 'lr' w następujący sposób-

res = PQexec(db, command);
latest_refresh = time(0);
if (PQresultStatus(res) == PGRES_COMMAND_OK) {
 0
Author: Park JongBum,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-02-13 20:19:06