Uzyskiwanie daty ostatniej modyfikacji tabeli bazy danych PostgreSQL

35

Próbuję się dowiedzieć, kiedy moja tabela została zmodyfikowana, sprawdzając datę modyfikacji pliku, jak opisano w tej odpowiedzi . Ale wynik nie zawsze jest poprawny. Data modyfikacji pliku aktualizuje się w kilka minut po aktualizacji mojej tabeli. Czy to jest prawidłowe zachowanie? Czy PostgreSQL przechowuje modyfikacje tabeli w jakiejś pamięci podręcznej, a następnie opróżnia ją na dysk twardy?

Jak więc uzyskać poprawną datę ostatniej modyfikacji tabeli (załóżmy, że automatyczne modyfikacje próżni też są w porządku)?

Używam PostgreSQL 9.2 pod Linux Centos 6.2 x64.

motek
źródło
4
Nie sądzę, aby czas modyfikacji pliku był wiarygodny. Może to również ulec zmianie z powodu autovacuum. Jedynym niezawodnym sposobem jest przechowywanie znacznika czasu modyfikacji w tabeli, obsługiwanego przez wyzwalacz.
a_horse_with_no_name
Jednym z pomysłów byłoby to, że informacje przechowywane w plikach WAL są zapisywane w plikach danych jakiś czas (krótszy lub dłuższy) po dokonaniu transakcji. Jeśli chcesz, możesz nazwać to pamięcią podręczną :) W przeciwnym razie popieram to, co powiedział @a_horse_with_no_name.
dezso

Odpowiedzi:

35

Nie ma wiarygodnego, autorytatywnego zapisu czasu ostatniej modyfikacji tabeli. Korzystanie z relfilenode jest nieprawidłowe z wielu powodów:

  • Zapisy są początkowo zapisywane w dzienniku głowicy zapisu (WAL), a następnie leniwie na stosie (pliki tabel). Gdy rekord znajduje się w WAL, Pg nie spieszy się z zapisaniem go na stercie i może nawet nie zostać zapisany do następnego punktu kontrolnego systemu;

  • Większe tabele mają wiele widelców, należy sprawdzić wszystkie widelce i wybrać najnowszy znacznik czasu;

  • Prosty SELECTmoże generować działanie zapisu do podstawowej tabeli ze względu na ustawienie bitu podpowiedzi;

  • autovaccum i inna konserwacja, która nie zmienia widocznych danych użytkownika, nadal modyfikuje pliki relacji;

  • niektóre operacje, na przykład vaccum full, zastąpią relfilenode. To może nie być to, czego oczekujesz, jeśli próbujesz spojrzeć na to jednocześnie bez odpowiedniej blokady.

Kilka opcji

Jeśli nie potrzebujesz niezawodności, możesz potencjalnie wykorzystać informacje w pg_stat_databasei pg_stat_all_tables. Mogą dać ci czas ostatniego resetu statystyk oraz statystyki aktywności od czasu ostatniego resetowania statystyk. Nie mówi ci, kiedy była ostatnia aktywność, tyle że od ostatniego resetu statystyk i nie ma informacji o tym, co się stało przed zresetowaniem statystyk. Więc jest ograniczony, ale już tam jest.

Jedną z opcji niezawodnego działania jest użycie wyzwalacza do aktualizacji tabeli zawierającej czasy ostatniej modyfikacji dla każdej tabeli. Pamiętaj, że spowoduje to szeregowanie wszystkich zapisów do tabeli , niszcząc współbieżność. Doda to również sporo kosztów ogólnych do każdej transakcji. Nie polecam tego

Nieco mniej okropną alternatywą jest użycie LISTENi NOTIFY. Niech zewnętrzny proces demona połączy się z PostgreSQL i LISTENdla zdarzeń. Użyj ON INSERT OR UPDATE OR DELETEwyzwalaczy, aby wysłać NOTIFYs, gdy zmienia się tabela, z oid tabeli jako ładunkiem powiadomienia. Są one wysyłane po zatwierdzeniu transakcji. Twój demon może gromadzić powiadomienia o zmianach i leniwie zapisywać je z powrotem do tabeli w bazie danych. Jeśli system ulegnie awarii, stracisz rejestr ostatnich modyfikacji, ale to jest ok, traktujesz wszystkie tabele jako właśnie zmodyfikowane, jeśli zaczynasz po awarii.

Aby uniknąć najgorszych problemów z współbieżnością, zamiast tego można rejestrować znaczniki czasu zmiany za pomocą before insert or update or delete or truncate on tablename for each statement executewyzwalacza, uogólnionego w celu uwzględnienia oid relacji jako parametru. Spowodowałoby to wstawienie (relation_oid, timestamp)pary do tabeli rejestrowania zmian. Następnie masz proces pomocniczy na oddzielnym połączeniu lub wywoływany okresowo przez aplikację, agregujesz tę tabelę, aby uzyskać najnowsze informacje, scalasz ją z tabelą podsumowań najnowszych zmian i obcinasz tabelę dziennika. Jedyną zaletą tego rozwiązania w porównaniu z metodą nasłuchiwania / powiadamiania jest to, że nie traci informacji w przypadku awarii - ale jest jeszcze mniej wydajny.

Innym rozwiązaniem może być napisać funkcję przedłużacza C, który używa (np) ProcessUtility_hook, ExecutorRun_hookitp do zmian stołowych pułapkę i leniwie statystykach aktualizacji. Nie zastanawiałem się, czy byłoby to praktyczne; spójrz na różne opcje _hook w źródłach.

Najlepszym sposobem byłoby załatanie kodu statystycznego, aby zapisać te informacje i przesłać łatkę do PostgreSQL w celu włączenia do rdzenia. Nie zaczynaj od pisania kodu; podnieś swój pomysł na -hakerów, gdy tylko pomyślisz o tym wystarczająco, aby mieć dobrze zdefiniowany sposób na zrobienie tego (tj. zacznij od przeczytania kodu, nie po prostu pytaj „jak mam ...”). Przydałoby się dodać czasy ostatniej aktualizacji pg_stat_..., ale musiałbyś przekonać społeczność, że warto było to narzucić, lub podać sposób opcjonalnego śledzenia - i musiałbyś napisać kod, aby zachować statystyki i prześlij łatkę , ponieważ tylko ktoś, kto chce tej funkcji, będzie się tym przejmował.

Jak bym to zrobił

Gdybym musiał to zrobić i nie miałbym czasu, aby napisać łatkę, aby zrobić to poprawnie, prawdopodobnie skorzystałbym z metody słuchania / powiadamiania opisanej powyżej.

Aktualizacja znaczników czasu zatwierdzania PostgreSQL 9.5

Aktualizacja : PostgreSQL 9.5 ma znaczniki czasu zatwierdzenia . Jeśli masz je włączone w postgresql.conf(i robiłeś to również w przeszłości), możesz sprawdzić znacznik czasu zatwierdzenia dla wiersza z największym, xminaby przybliżać czas ostatniej modyfikacji. Jest to tylko przybliżenie, ponieważ jeśli najnowsze wiersze zostaną usunięte, nie zostaną zliczone.

Ponadto zapisy datownika zatwierdzenia są przechowywane tylko przez ograniczony czas. Więc jeśli chcesz powiedzieć, kiedy tabela, która nie jest dużo zmodyfikowana, jest zmodyfikowana, odpowiedź będzie brzmiała „nie wiem, jakiś czas temu”.

Craig Ringer
źródło
17

PostgreSQL 9.5 pozwala nam śledzić ostatnio zmodyfikowane zatwierdzenie.

  1. Sprawdź, czy zatwierdzenie ścieżki jest włączone lub wyłączone przy użyciu następującego zapytania

    show track_commit_timestamp;
  2. Jeśli zwróci „ON”, przejdź do kroku 3, w przeciwnym razie zmodyfikuj plik postgresql.conf

    cd /etc/postgresql/9.5/main/
    vi postgresql.conf

    Zmiana

    track_commit_timestamp = off

    do

    track_commit_timestamp = on

    Uruchom ponownie system

    Powtórz krok 1.

  3. Użyj następującego zapytania, aby śledzić ostatnie zatwierdzenie

    SELECT pg_xact_commit_timestamp(xmin), * FROM  YOUR_TABLE_NAME;
    
    SELECT pg_xact_commit_timestamp(xmin), * FROM YOUR_TABLE_NAME where COLUMN_NAME=VALUE;
Thirumal
źródło
1
Nie musisz ponownie uruchamiać systemu w kroku 2. po prostu zrestartuj proces. np sudo service postgresql restart.
ijoseph
3

Tak, można się tego spodziewać - dane o zmianie są natychmiast zapisywane w dzienniku transakcji. Pliki danych można aktualizować z opóźnieniem checkpoint_timeout (domyślnie 5 minut). Postgres nie utrzymuje się na stałe za każdym razem, gdy o to poprosisz.

Pavel Stehule
źródło
Nie jestem pewien, czy rozumiem, jak to odpowiada na pytanie. Tak, dane są przechowywane w dzienniku transakcji, ale to nie znaczy, że można łatwo uzyskać czas modyfikacji dla określonej tabeli ( jeśli ta zawartość jest nadal w dzienniku, można go przeanalizować, ale rzeczy są odtwarzane raczej szybko).
Charles Duffy
Oczywiście, możesz uzyskać wszystkie niezbędne informacje z dziennika, ale pytania zostały skierowane do mtime plików danych - aktualizacja plików danych może być dość losowa - kilka sekund - kilka minut (maksymalnie 1 godzina) po zatwierdzeniu.
Pavel Stehule,
Własna próba OP polegała na przeglądaniu plików, ale ich prawdziwym zamiarem jest oczywiście zdobycie mtimeu na stole. Ale tak, rozumiem skąd pochodzisz (wyjaśniając, dlaczego to, co robili, nie działało) teraz.
Charles Duffy
2

Mam prawie takie same wymagania, aby utrzymać pamięć podręczną niektórych tabel w aplikacji klienckiej. Mówię prawie , ponieważ tak naprawdę nie muszę znać czasu ostatniej modyfikacji, ale tylko po to, aby wykryć, czy coś się zmieniło od czasu ostatniej synchronizacji pamięci podręcznej.

Oto moje podejście:

Pod warunkiem, że masz kolumnę id(PK), created_on(wstawianie znacznika czasu) i updated_on(aktualizuj znacznik czasu, może być NULL) w każdej tabeli, możesz

SELECT id,greatest(created_on,updated_on) FROM %s ORDER BY greatest(created_on,updated_on) DESC LIMIT 1;

Jeśli połączysz to i dodasz liczbę wierszy, możesz zbudować znacznik wersji, który będzie wyglądał count:id#timestampi będzie unikalny dla każdej wersji danych w tabeli.

Laurent
źródło