Möglich, die maximale Anzahl von Datensätzen in postgresql zu halten?

9

Grundsätzlich wird ein Teil unserer Postgresql-Tabelle verwendet, um Serverzugriffsprotokolle zu führen, und als solche kann dies manchmal während der Produktion ziemlich groß werden. Gibt es eine Möglichkeit, in postgresql die maximale Anzahl von Datensätzen festzulegen, die eine Tabelle haben kann, und den ältesten Datensatz zu verschieben?

Jharwood
quelle

Antworten:

12

Sie können einen Auslöser definieren, um die gewünschte Zeilennummer beizubehalten:

CREATE OR REPLACE FUNCTION trf_keep_row_number_steady()
RETURNS TRIGGER AS
$body$
BEGIN
    -- delete only where are too many rows
    IF (SELECT count(id) FROM log_table) > rownum_limit
    THEN 
        -- I assume here that id is an auto-incremented value in log_table
        DELETE FROM log_table
        WHERE id = (SELECT min(id) FROM log_table);
    END IF;
END;
$body$
LANGUAGE plpgsql;

CREATE TRIGGER tr_keep_row_number_steady 
AFTER INSERT ON log_table
FOR EACH ROW EXECUTE PROCEDURE trf_keep_row_number_steady();

Dies ist wahrscheinlich nicht die Option mit der besten Leistung, aber sobald Sie das Limit erreicht haben, wird es nie überschritten. Wenn Platz für Schwankungen vorhanden ist, können Sie die Zeilennummer regelmäßig überprüfen und überschüssige Zeilen von Anfang an löschen.

BEARBEITEN: Wenn Sie wirklich große Protokolle haben (z. B. eine Million pro Monat),kann die Partitionierung die einfachste Lösung sein. Sie können dann einfach die unnötigen Tabellen löschen (sagen Sie womax(timestamp) < CURRENT_DATE - 1 year). Sie können Ihren Zeitstempel (oder ein abgeleitetes Datum) als Bedingung für die Bereichspartitionierung verwenden .

Aber seien Sie vorsichtig, bevor Sie alte Protokolle verwerfen. Sind Sie sicher, dass Sie diese niemals brauchen werden?

dezso
quelle
Wir können es regelmäßig ausführen, und wir sind sicher, dass wir sie nicht benötigen werden, sobald die Tabelle groß genug ist, um dies zu erfordern. Ich versuche nur, die DB-Wartung so weit wie möglich zu automatisieren :)
Jharwood
Ich hatte auch gehofft, dass Postgres erkennen könnte, welches selbst älter ist, aber wenn wir keine IDs haben, könnte es unser Zeitstempelfeld "2012-06-22 17: 17: 52.692514"
Jharwood
@ Jharwood - meine Antwort bearbeitet. Bitte sagen Sie mir, wenn Sie weitere Details benötigen.
Dekso
2
+1 auf den Partitionierungsvorschlag. Wenn Sie eine Zählung durchführen möchten, ohne jedes Mal den extremen Aufwand für das Scannen der Tabelle zu verursachen, können Sie pg_class.reltuples als Näherungswert verwenden oder Trigger verwenden, um eine Zählung in einer "Steuertabelle" zu verwalten.
kgrittn
4

Ich habe eine allgemeinere, tabellenunabhängige Funktion erstellt.

CREATE OR REPLACE FUNCTION keep_row_number_steady()
RETURNS TRIGGER AS
$body$
DECLARE
    tab text;
    keyfld text;
    nritems INTEGER;
    rnd DOUBLE PRECISION;
BEGIN
    tab := TG_ARGV[0];
    keyfld := TG_ARGV[1];
    nritems := TG_ARGV[2]; 
    rnd := TG_ARGV[3];

    IF random() < rnd
    THEN 
        EXECUTE(format('DELETE FROM %s WHERE %s < (SELECT %s FROM %s ORDER BY %s DESC LIMIT 1 OFFSET %s)', tab, keyfld, keyfld, tab, keyfld, nritems));
    END IF;
    RETURN NULL;
END;
$body$
LANGUAGE plpgsql;

CREATE TRIGGER log_table_keep_row_number_steady_trigger
AFTER INSERT ON log_table
FOR EACH STATEMENT EXECUTE PROCEDURE keep_row_number_steady('log_table', 'id', 1000, 0.1);

Die Funktion benötigt 4 Parameter:

  • Registerkarte: Tabellenname
  • keyfld: numerisches, progressives Schlüsselfeld
  • nritems: Anzahl der Elemente, die aufbewahrt werden sollen
  • rnd: Zufallszahl von 0 bis 1; Je größer es ist, desto häufiger wird der Tisch gereinigt (0 = nie, 1 = immer, 0,1 = 10% der Fälle).

Auf diese Weise können Sie erstellen, wie viele Trigger dieselbe Funktion aufrufen sollen.

Hoffe das hilft.

Gurte
quelle
0

Ich habe diesen Prozess erstellt und von PG Agent (oder Windows-Job oder Cron-Job abhängig) ausgeführt. Ich kann mehr Zeilen haben, dadurch bleibt meine Protokolltabelle nicht zu groß. Spart den Overhead eines Triggers.

CREATE or replace FUNCTION activitylogcleanup(_MaxRows int) RETURNS void
    LANGUAGE plpgsql
    AS $$
DECLARE
   minid    int;
BEGIN
    SELECT logid into minid FROM activitylogapplication 
     order by logid desc limit 1 OFFSET _MaxRows;

    if not found then 
        return;
    END IF; 

    Delete from activitylogapplication where logid < minid;
END;
$$;

Ron H.
quelle