Frage

Ich habe eine Tabelle mit ungefähr 3,1 Millionen Zeilen mit der folgenden Definition und den folgenden Indizes:

CREATE TABLE digiroad_liikenne_elementti (
    ogc_fid serial NOT NULL,
    wkb_geometry geometry(Geometry,4258),
    tiee_tila numeric(9,0),
    vaylatyypp numeric(9,0),
    toiminnall numeric(9,0),
    eurooppati character varying(254),
    kansalline numeric(9,0),
    tyyppi numeric(9,0),
    liikennevi numeric(9,0),
    ens_talo_o numeric(9,0),
    talonumero numeric(9,0),
    ens_talo_v numeric(9,0),
    oik_puol_t character varying(254),
    tieosan_ta numeric(9,0),
    viim_talo_ numeric(9,0),
    viim_tal_1 numeric(9,0),
    vas_puol_t character varying(254),
    laut_tyypp numeric(9,0),
    lautta_lii numeric(9,0),
    inv_paalu_ numeric(19,11),
    inv_paal_1 numeric(19,11),
    liitalue_o numeric(9,0),
    ketju_oid numeric(9,0),
    tietojoukk numeric(9,0),
    ajoratanum numeric(4,0),
    viite_guid character varying(254),
    "timestamp" date,
    tiee_kunta numeric(9,0),
    toissij_ti character varying(254),
    viite_oid numeric(9,0),
    k_elem_id numeric(9,0),
    region character varying(40) DEFAULT 'REGION'::character varying,
    CONSTRAINT digiroad_liikenne_elementti_pkey PRIMARY KEY (ogc_fid)
);

CREATE INDEX digiroad_liikenne_elementti_wkb_geometry_geom_idx
  ON digiroad_liikenne_elementti USING gist (wkb_geometry);

CREATE INDEX dle_k_elem_id_idx
  ON digiroad_liikenne_elementti USING btree (k_elem_id);

CREATE INDEX dle_ogc_fid_idx
  ON digiroad_liikenne_elementti USING btree (ogc_fid);

CREATE INDEX dle_region_idx
  ON digiroad_liikenne_elementti USING btree (region COLLATE pg_catalog."default");

Eine weitere Tabelle mit 8,6 Millionen Zeilen enthält Attribute für die Zeilen der ersten Tabelle, die Tabellen können mit verknüpft werden k_elem_id UND region.

CREATE TABLE digiroad_segmentti (
    ogc_fid serial NOT NULL,
    wkb_geometry geometry(Geometry,4258),
    segm_tila numeric(9,0),
    tyyppi numeric(9,0),
    loppupiste numeric(19,11),
    alkupiste numeric(19,11),
    vaikutuska numeric(9,0),
    vaikutussu numeric(9,0),
    vaikutusai character varying(254),
    tieosanume numeric(19,11),
    tienumero numeric(9,0),
    dyn_arvo numeric(9,0),
    dyn_tyyppi numeric(9,0),
    omistaja_t numeric(9,0),
    pysakki_va numeric(9,0),
    pysakki_ty numeric(9,0),
    pysakki_su numeric(9,0),
    pysakki_ka numeric(9,0),
    pysakki_yl character varying(254),
    palvelu_pa numeric(9,0),
    toissijain numeric(9,0),
    siltataitu numeric(9,0),
    rdtc_tyypp numeric(9,0),
    rdtc_alaty numeric(9,0),
    rdtc_paikk numeric(19,11),
    rdtc_luokk numeric(9,0),
    rdtc_liitt character varying(254),
    palvelu_ob numeric(9,0),
    ketju_oid numeric(9,0),
    tietojoukk numeric(9,0),
    ajoratanum numeric(4,0),
    viite_guid character varying(254),
    "timestamp" date,
    sivusiirty numeric(19,11),
    toissij_ti character varying(254),
    viite_oid numeric(9,0),
    k_elem_id numeric(9,0),
    region character varying(40) DEFAULT 'REGION'::character varying,
    CONSTRAINT digiroad_segmentti_pkey PRIMARY KEY (ogc_fid)
);

CREATE INDEX digiroad_segmentti_wkb_geometry_geom_idx
  ON digiroad_segmentti USING gist (wkb_geometry);

CREATE INDEX ds_dyn_arvo_idx
  ON digiroad_segmentti USING btree (dyn_arvo);

CREATE INDEX ds_dyn_tyyppi_idx
  ON digiroad_segmentti USING btree (dyn_tyyppi);

CREATE INDEX ds_k_elem_id_idx
  ON digiroad_segmentti USING btree (k_elem_id);

CREATE INDEX ds_ogc_fid_idx
  ON digiroad_segmentti USING btree (ogc_fid);

CREATE INDEX ds_region_idx
  ON digiroad_segmentti USING btree (region COLLATE pg_catalog."default");

CREATE INDEX ds_tyyppi_idx
  ON digiroad_segmentti USING btree (tyyppi);

Ich versuche, die Zeilen der ersten Tabelle (mit einigen Änderungen) in eine neue Tabelle einzufügen:

CREATE TABLE edge_table (
    id serial NOT NULL,
    geom geometry,
    source integer,
    target integer,
    km double precision,
    kmh double precision DEFAULT 60,
    kmh_winter double precision DEFAULT 50,
    cost double precision,
    cost_winter double precision,
    reverse_cost double precision,
    reverse_cost_winter double precision,
    x1 double precision,
    y1 double precision,
    x2 double precision,
    y2 double precision,
    k_elem_id integer,
    region character varying(40),
    CONSTRAINT edge_table_pkey PRIMARY KEY (id)
);

Da das Ausführen einer einzelnen Einfügeanweisung lange dauern würde und ich nicht sehen könnte, ob die Anweisung feststeckt oder so, habe ich beschlossen, dies in kleineren Abschnitten innerhalb einer Schleife in einer Funktion zu tun.

Die Funktion sieht so aus:

DROP FUNCTION IF EXISTS insert_function();
CREATE OR REPLACE FUNCTION insert_function()
    RETURNS VOID AS
    $$
DECLARE
    const_type_1 CONSTANT int := 5;
    const_type_2 CONSTANT int := 11;
    i int := 0;
    row_count int;
BEGIN

    CREATE TABLE IF NOT EXISTS edge_table (
        id serial PRIMARY KEY,
        geom geometry,
        source integer,
        target integer,
        km double precision,
        kmh double precision DEFAULT 60,
        kmh_winter double precision DEFAULT 50,
        cost double precision,
        cost_winter double precision,
        reverse_cost double precision,
        reverse_cost_winter double precision,
        x1 double precision,
        y1 double precision,
        x2 double precision,
        y2 double precision,
        k_elem_id integer,
        region varchar(40)
    );


    batch_size := 1000;
    SELECT COUNT(*) FROM digiroad_liikenne_elementti INTO row_count;

    WHILE i*batch_size < row_count LOOP

        RAISE NOTICE 'insert: % / %', i * batch_size, row_count;

        INSERT INTO edge_table (kmh, kmh_winter, k_elem_id, region)
        SELECT      CASE WHEN DS.dyn_arvo IS NULL THEN 60 ELSE DS.dyn_arvo END,
                    CASE WHEN DS.dyn_Arvo IS NULL THEN 50 ELSE DS.dyn_arvo END,
                    DR.k_elem_id,
                    DR.region
        FROM        (
                        SELECT  DLE.k_elem_id,
                                DLE.region,
                        FROM    digiroad_liikenne_elementti DLE
                        WHERE   DLE.ogc_fid >= i * batch_size
                                AND
                                DLE.ogc_fid <= i * batch_size + batch_size
                    ) AS DR
                    LEFT JOIN
                    digiroad_segmentti DS ON
                        DS.k_elem_id = DR.k_elem_id
                        AND
                        DS.region = DR.region
                        AND
                        DS.tyyppi = const_type_1
                        AND
                        DS.dyn_tyyppi = const_type_2;

        i := i + 1;
    END LOOP;
END;
$$
LANGUAGE 'plpgsql' VOLATILE STRICT;

Das Problem ist, dass es ziemlich schnell anfängt, die Schleifen zu durchlaufen, sich dann aber irgendwann zu einem Kriechen verlangsamt.Wenn es langsamer wird, steigt gleichzeitig die Festplattennutzung in meinem Windows 8 Task-Manager auf 99%, sodass ich vermute, dass dies irgendwie mit dem Problem zusammenhängt.

Ausführen des INSERT aussage für sich allein mit einem zufälligen Wert von i wird sehr schnell ausgeführt, sodass das Problem anscheinend nur auftritt, wenn es in der Schleife innerhalb einer Funktion ausgeführt wird.Hier ist der EXPLAIN (ANALYZE,BUFFERS) aus einer solchen Einzelausführung:

Insert on edge_table  (cost=0.86..361121.68 rows=1031 width=23) (actual time=3405.101..3405.101 rows=0 loops=1)
  Buffers: shared hit=36251 read=3660 dirtied=14
  ->  Nested Loop Left Join  (cost=0.86..361121.68 rows=1031 width=23) (actual time=61.901..3377.609 rows=986 loops=1)
        Buffers: shared hit=32279 read=3646
        ->  Index Scan using dle_ogc_fid_idx on digiroad_liikenne_elementti dle  (cost=0.43..85.12 rows=1031 width=19) (actual time=31.918..57.309 rows=986 loops=1)
              Index Cond: ((ogc_fid >= 200000) AND (ogc_fid < 201000))
              Buffers: shared hit=27 read=58
        ->  Index Scan using ds_k_elem_id_idx on digiroad_segmentti ds  (cost=0.44..350.16 rows=1 width=23) (actual time=2.861..3.337 rows=0 loops=986)
              Index Cond: (k_elem_id = dle.k_elem_id)
              Filter: ((tyyppi = 5::numeric) AND (dyn_tyyppi = 11::numeric) AND (vaikutussu = 3::numeric) AND ((region)::text = (dle.region)::text))
              Rows Removed by Filter: 73
              Buffers: shared hit=31266 read=3588
Total runtime: 3405.270 ms

Auf meinem System wird PostgreSQL 9.3.5 unter Windows 8 mit 8 GB RAM ausgeführt.

Ich habe mit verschiedenen Stapelgrößen experimentiert, die Abfrage auf unterschiedliche Weise durchgeführt und die Speichervariablen in der Postgres-Konfiguration erhöht, aber nichts scheint das Problem wirklich gelöst zu haben.

Konfigurationsvariablen, die von ihren Standardwerten geändert wurden:

shared_buffers = 2048MB
work_mem = 64MB
effective_cache_size = 6000MB

Ich würde gerne herausfinden, was das verursacht und was dagegen getan werden könnte.

War es hilfreich?

Lösung

Beim Erstellen eines neuer Tisch vermeiden Sie die Kosten für das Schreiben Vorausschreibprotokoll (WAL) komplett mit CREATE TABLE AS.
Sehen @Kassandrys Antwort für eine Erklärung, wie WAL dazu beiträgt.

CREATE OR REPLACE FUNCTION insert_function()
  RETURNS void AS
$func$
DECLARE
   const_type_1 CONSTANT int := 5;
   const_type_2 CONSTANT int := 11;
BEGIN    
   CREATE SEQUENCE edge_table_id_seq;

   CREATE TABLE edge_table AS
   SELECT nextval('edge_table_id_seq'::regclass)::int AS id
        , NULL::geometry         AS geom
        , NULL::integer          AS source
        , target::integer        AS target
        , NULL::float8           AS km
        , COALESCE(DS.dyn_arvo::float8, float8 '60') AS kmh
        , COALESCE(DS.dyn_Arvo::float8, float8 '50') AS kmh_winter
        , NULL::float8           AS cost
        , NULL::float8           AS cost_winter
        , NULL::float8           AS reverse_cost
        , NULL::float8           AS reverse_cost_winter
        , NULL::float8           AS x1
        , NULL::float8           AS y1
        , NULL::float8           AS x2
        , NULL::float8           AS y2
        , D.k_elem_id::integer   AS k_elem_id
        , D.region::varchar(40)  AS region
   FROM   digiroad_liikenne_elementti D
   LEFT   JOIN digiroad_segmentti DS
             ON DS.k_elem_id = D.k_elem_id
            AND DS.region = D.region
            AND DS.tyyppi = const_type_1
            AND DS.dyn_tyyppi = const_type_2;

   ALTER TABLE edge_table
      ADD CONSTRAINT edge_table_pkey PRIMARY KEY(id)
    , ALTER COLUMN id SET NOT NULL
    , ALTER COLUMN id SET DEFAULT nextval('edge_table_id_seq'::regclass)
    , ALTER COLUMN kmh SET DEFAULT 60
    , ALTER COLUMN kmh_winter SET DEFAULT 50;

   ALTER SEQUENCE edge_table_id_seq OWNED BY edge_table.id;    
END
$func$ LANGUAGE plpgsql;

Dokumentation:

Abgesehen davon, dass die Zeit für die Verarbeitung durch den Archivierer oder WAL-Absender vermieden wird die WAL-Daten werden dadurch bestimmte Befehle tatsächlich schneller machen, weil sie so konzipiert sind, dass sie überhaupt kein WAL schreiben, wenn wal_level is minimal.(Sie können die Crashsicherheit billiger garantieren, indem sie eine fsync am Ende als durch Schreiben von WAL.) Dies gilt für Folgendes Menübefehle:

  • CREATE TABLE AS SELECT

  • CREATE INDEX (und Varianten wie ALTER TABLE ADD PRIMARY KEY)

  • ALTER TABLE SET TABLESPACE

  • CLUSTER

  • COPY FROM, wenn die Zieltabelle früher in derselben Transaktion erstellt oder abgeschnitten wurde

Auch wichtig

  • CREATE TABLE AS macht es unmöglich, den Pseudotyp zu verwenden serial direkt.Aber da das nur ein "Makro" ist, können Sie stattdessen alles von Hand machen:Erstellen Sie die Sequenz, verwenden Sie sie zum Generieren id Wertvorstellungen.Legen Sie abschließend die Spaltenstandardeinstellung fest und legen Sie fest, dass die Spalte die Sequenz besitzt.Zusammenhang:

  • Der plpgsql-Funktions-Wrapper ist optional (praktisch für die wiederholte Verwendung), Sie können einfach ausführen einfaches SQL in einer Transaktion: BEGIN; ... COMMIT;

  • Hinzufügen der PRIMARY KEY nach das Einfügen der Daten ist auch schneller, da das Erstellen des (zugrunde liegenden) Index in einem Stück schneller ist als das schrittweise Hinzufügen von Werten.

  • Du hattest eine logischer Fehler in Ihrer Partitionierung:

    WHERE DLE.ogc_fid >= i * batch_size
    AND   DLE.ogc_fid <= i * batch_size + batch_size
    

    Die letzte Zeile würde sich mit der nächsten Partition überlappen, die Zeile würde wiederholt eingefügt, was zu einer eindeutigen Verletzung in der PK führt.Die Verwendung von < statt <= würde das beheben - aber ich habe die Partitionierung komplett entfernt.

  • Wenn Sie dies wiederholt ausführen, wird ein mehrspaltiger Index auf digiroad_segmentti (k_elem_id, tyyppi, dyn_tyyppi, region) könnte sich je nach Datenverteilung auszahlen.

Kleinigkeiten

  • Zitiere die Sprache nicht plpgsql name, es ist eine Kennung.
  • Es wäre sinnlos, eine Funktion ohne Parameter als zu markieren STRICT.
  • VOLATILE ist die Standardeinstellung und nur Rauschen.
  • Verwenden COALESCE um einen Standardwert für Nullwerte bereitzustellen.

  • Einige Ihrer double precision (float8) spalten könnten besser funktionieren als integer da du meistens hattest numeric (9,0) in Ihren alten Tischen, die wahrscheinlich durch die billigere Ebene ersetzt werden können integer.

  • Spalte region varchar(40) sieht aus wie ein Kandidat für die Normalisierung (es sei denn, Regionen sind größtenteils einzigartig?) Erstellen Sie eine Regionstabelle und verwenden Sie einfach region_id als FK-Spalte in der Haupttabelle.

Andere Tipps

Wenn Sie nur das geändert haben shared_buffers,work_mem, und effective_cache_size konfigurationsvariablen, dann laufen Sie wahrscheinlich noch mit checkpoint_segments=3.

In diesem Fall haben Sie nur drei WAL-Segmente und müssen diese daher kontinuierlich recyceln, wodurch jedes Mal Schreibvorgänge in die Datendateien erzwungen werden, was zu einer enormen Menge an E / A-Aktivität führt und Ihren Computer mit Sicherheit zum Crawlen verlangsamen kann.Sie können das Checkpointing-Verhalten überprüfen, indem Sie im Protokoll nach der Phrase suchen checkpoints are occurring too frequently.Sie können auch sehen, was sie tun, indem Sie aktivieren log_checkpoints=on in Ihrem postgresql.konf

Ich würde empfehlen, Ihre zu ändern checkpoint_segments zu etwas Größerem, wie 40, und die checkpoint_completion_target auf 0,9, um zu versuchen, das von Ihnen beschriebene Verhalten zu glätten.

Die Einstellungen werden hier in der PostgreSQL-Dokumentation für 9.3 in der Protokoll vorausschreiben Abschnitt.=)

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit dba.stackexchange
scroll top