PostgreSQL-Partitionshierarchie-Design für die zentralisierte Protokollierung

7

Ich möchte die Protokollierung von allen meinen Servern in einer zentralen Postgresql-Datenbank einrichten. Es ist sinnvoll, Protokolle nach Datum oder Host löschen zu können. Daher möchte ich sie mit Partitionierung einrichten , aber mit einer mehrschichtigen Partition:host-service inherits service, host-service-yyyymm inherits host-service.

Ich habe ein Beispielschema für einen bestimmten Dienst ausgearbeitet (in diesem Fall PHP-Fehler) und suche einige PostgreSQL-Experten, um es auf offensichtliche Leistungsengpässe zu kritisieren.

// SET UP MASTER PHP LOG TABLE //
CREATE TABLE php (
 log_id     int not null,
 host       char(5),    
 logdate     date not null,
 message        text
);

// SET UP HOST-SPECIFIC 'PARTITIONS' //
CREATE TABLE host1_php (
 CHECK ( host = 'host1' )
) INHERITS (php);

CREATE TABLE host2_php (
 CHECK ( host = 'host2' )
) INHERITS (php);

// SET UP HOST-SPECIFIC TIME 'PARTITIONS' //
CREATE TABLE host1_php_2011m12 (
 CHECK ( logdate >= DATE '2011-12-01' AND logdate < DATE '2012-01-01' )
) INHERITS (host1_php);

CREATE TABLE host1_php_2012m01 (
 CHECK ( logdate >= DATE '2012-01-01' AND logdate < DATE '2012-02-01' )
) INHERITS (host1_php);

CREATE TABLE host2_php_2011m12 (
 CHECK ( logdate >= DATE '2011-12-01' AND logdate < DATE '2012-01-01' )
) INHERITS (host2_php);

CREATE TABLE host2_php_2012m01 (
 CHECK ( logdate >= DATE '2012-01-01' AND logdate < DATE '2012-02-01' )
) INHERITS (host2_php);

CREATE INDEX host1_php_2011m12_logdate ON host1_php_2011m12 (logdate);
CREATE INDEX host1_php_2012m01_logdate ON host1_php_2012m01 (logdate);
CREATE INDEX host2_php_2011m12_logdate ON host2_php_2011m12 (logdate);
CREATE INDEX host2_php_2012m01_logdate ON host2_php_2012m01 (logdate);

Ich werde auch Dienste wie Apache-Zugriff / Fehler hinzufügen.

Ich denke, ich muss auslösen, um in den host_service einzufügen (um die Host-Check-Einschränkung zu verwenden), und dann muss jeder host_service auslösen, um in die Tabelle host_service_yyyymm einzufügen.

Was kann ich von einem solchen Trigger- / Partitionsschema in Bezug auf die Leistung erwarten?

Einige zusätzliche Informationen, die im Chat besprochen wurden:

  • PostgreSQL Version 9.1.2
  • Datenabfragen werden nicht sehr häufig durchgeführt, hauptsächlich im aktuellen Datenmonat für mehrere Hosts.
  • PHP ist nur ungefähr 1 pro Minute Insert, aber der gesamte Apache wird wahrscheinlich auf allen Hosts ungefähr 300-500 pro Sekunde betragen.
Derek Downey
quelle
Hey @DTest. Ich schaue mir deine Sachen an und denke an meine MRG_MyISAM-Tage zurück. Da ich ein Neuling bei PostgresSQL bin, ist hier eine Frage: Benötigt die phpTabelle einen Index für (host,logdate)und erben die Kinder den Index?
RolandoMySQLDBA
Hah, MERGEgenau daran habe ich gedacht, als ich über die Partitionierung in Postgre nachgelesen habe ... Nein, keine Indizes zum Master
Derek Downey
1
Überprüfen Sie Ihre CREATE INDEXAussagen. Sollten es nicht verschiedene Tabellen sein, eine für jede Partition?
RolandoMySQLDBA

Antworten:

6

Was kann ich von einem solchen Trigger- / Partitionsschema in Bezug auf die Leistung erwarten?

Kontextwechsel bedeuten, dass die Verwendung von Triggern immer viel mehr CPU als eine einfache benötigt insert. Das folgende Skript kann verwendet werden, um die Auswirkungen zu quantifizieren. Außerdem wird das automatische Erstellen von Partitionen mithilfe von Triggern demonstriert und die Leistung in beiden Fällen verglichen.

Bitte beachten Sie, dass ich keine Indizierung oder Berücksichtigung von updateAussagen aufgenommen habe.

begin; 
set role dba;
create role stack;
grant stack to dba;
create schema authorization stack;
set role stack;
--
--******** the above creates a nice clean schema as a test area
--
set client_min_messages to warning; --******** or you get a lot of "NOTICE:  merging column "xyz" with inherited definition" notices
--
create table phpheap(log_id serial not null, host text not null, logdate date not null, message text not null); --******** This table is used to compare 'insert' performance with that on the partitioned version
create table php(log_id serial not null, host text not null, logdate date not null, message text not null);
--
create function php_host_insert() returns trigger language plpgsql security definer as $$
begin 
  set search_path to 'stack';
  execute 'insert into php_'||new.host||'_'||to_char(new.logdate, 'YYYYmMM')||'(log_id, host, logdate, message) values($1, $2, $3, $4)' using new.log_id, new.host, new.logdate, new.message;
  return null;
exception when undefined_table then
  execute 'create table php_'||new.host||'_'||to_char(new.logdate, 'YYYYmMM')||'(log_id int not null, host text not null check(host='''||new.host||'''), logdate date not null check(to_char(logdate, ''YYYYmMM'')='''||to_char(new.logdate, 'YYYYmMM')||'''), message text not null) inherits (php_'||new.host||')';
  execute 'insert into php_'||new.host||'_'||to_char(new.logdate, 'YYYYmMM')||'(log_id, host, logdate, message) values($1, $2, $3, $4)' using new.log_id, new.host, new.logdate, new.message;
  return null;
end;$$;
--
create function php_insert() returns trigger language plpgsql security definer as $$
begin 
  set search_path to 'stack';
  execute 'insert into php_'||new.host||'(log_id, host, logdate, message) values($1, $2, $3, $4)' using new.log_id, new.host, new.logdate, new.message;
  return null; 
exception when undefined_table then
  execute 'create table php_'||new.host||'(log_id int not null, host text not null check(host='''||new.host||'''), logdate date not null, message text not null) inherits(php)';
  execute 'create trigger trig_insert_php_'||new.host||' before insert on php_'||new.host||' for each row execute procedure php_host_insert()';
  execute 'insert into php_'||new.host||'(log_id, host, logdate, message) values($1, $2, $3, $4)' using new.log_id, new.host, new.logdate, new.message;
  return null;
end;$$;
--
create trigger trig_insert_php before insert on php for each row execute procedure php_insert();
--
\timing on
insert into phpheap(host, logdate, message) select 'host1', current_date-(generate_series(-99999, 0, 1)/1000)::integer, repeat('hello',20);
--******** output
--INSERT 0 100000
--Time: 1102.140 ms
insert into php(host, logdate, message) select 'host1', current_date-(generate_series(-99999, 0, 1)/1000)::integer, repeat('hello',20);
--******** output
--INSERT 0 0
--Time: 35615.498 ms
insert into php(host, logdate, message) select 'host1', current_date-(generate_series(-99999, 0, 1)/1000)::integer, repeat('hello',20);
--******** output
--INSERT 0 0
--Time: 34074.579 ms
\timing off
--
--******** Now we replace the trigger functions with the 'normal' kind that don't auto-create partitions
--
create or replace function php_host_insert() returns trigger language plpgsql security definer as $$
begin 
  set search_path to 'stack';
  execute 'insert into php_'||new.host||'_'||to_char(new.logdate, 'YYYYmMM')||'(log_id, host, logdate, message) values($1, $2, $3, $4)' using new.log_id, new.host, new.logdate, new.message;
  return null;
end;$$;
--
create or replace function php_insert() returns trigger language plpgsql security definer as $$
begin 
  set search_path to 'stack';
  execute 'insert into php_'||new.host||'(log_id, host, logdate, message) values($1, $2, $3, $4)' using new.log_id, new.host, new.logdate, new.message;
  return null; 
end;$$;
--
\timing on
insert into php(host, logdate, message) select 'host1', current_date-(generate_series(-99999, 0, 1)/1000)::integer, repeat('hello',20);
--******** output
--INSERT 0 0
--Time: 28457.146 ms
\timing off
--
rollback;
Jack sagt, versuchen Sie es mit topanswers.xyz
quelle
Danke dafür. Ich spiele immer noch mit Ideen, aber ich mache möglicherweise den Heap für die Protokollsammlung und analysiere sie dann in ein partitioniertes Setup mit zusätzlichen Informationen, die einmal am Tag aus der Nachrichtenspalte gesammelt werden.
Derek Downey