문제

다음과 같은 정의와 인덱스가 포함된 약 310만 개의 행으로 구성된 테이블이 있습니다.

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");

860만 개의 행이 있는 또 다른 테이블에는 첫 번째 테이블의 행에 대한 속성이 포함되어 있으며 테이블을 조인할 수 있습니다. k_elem_id 그리고 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);

첫 번째 테이블의 행을 일부 수정하여 새 테이블에 삽입하려고 합니다.

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)
);

단일 insert 문을 실행하는 데 시간이 오래 걸리고 문이 멈춰 있는지 확인할 수 없기 때문에 함수의 루프 내부에서 더 작은 덩어리로 실행하기로 결정했습니다.

함수는 다음과 같습니다:

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;

문제는 루프를 매우 빠르게 통과하기 시작했지만 어느 시점에서는 크롤링 속도가 느려진다는 것입니다.속도가 느려지는 동시에 Windows 8 작업 관리자의 디스크 사용량이 최대 99%까지 올라가므로 이것이 문제와 관련이 있는 것으로 의심됩니다.

실행 INSERT 임의의 값을 사용하여 자체적으로 명령문을 작성합니다. i 매우 빠르게 실행되므로 함수 내부의 루프에서 실행할 때만 문제가 발생하는 것 같습니다.여기는 EXPLAIN (ANALYZE,BUFFERS) 그러한 단일 실행 중 하나에서:

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

내 시스템은 8GB RAM을 갖춘 Windows 8에서 PostgreSQL 9.3.5를 실행하고 있습니다.

다양한 배치 크기를 실험하고, 다양한 방식으로 쿼리를 수행하고, Postgres 구성에서 메모리 변수를 늘렸지만 실제로 문제를 해결한 것은 아무것도 없는 것 같습니다.

기본값에서 변경된 구성 변수:

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

이런 일이 발생하는 원인이 무엇인지, 이에 대해 어떤 조치를 취할 수 있는지 알고 싶습니다.

도움이 되었습니까?

해결책

생성할 때 새 테이블 글쓰기 비용을 피하세요 미리 쓰기 로그(WAL) 완전히 ~와 함께 CREATE TABLE AS.
보다 @Kassandry의 답변 WAL이 이에 대해 어떻게 설명하는지 설명합니다.

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;

문서:

Archiver 또는 WAL Sender가 WAL 데이터를 처리 할 시간을 피하는 것 외에도,이 작업을 수행하면 실제로 WAL을 작성하지 않도록 설계 되었기 때문에 특정 명령이 더 빨라집니다. wal_level ~이다 minimal.(충돌 안전을 보다 저렴하게 보장할 수 있습니다. fsync 마지막에 WAL을 쓰는 것보다.) 이는 다음에 적용됩니다 명령을:

  • CREATE TABLE AS SELECT

  • CREATE INDEX (그리고 다음과 같은 변형 ALTER TABLE ADD PRIMARY KEY)

  • ALTER TABLE SET TABLESPACE

  • CLUSTER

  • COPY FROM, 동일한 트랜잭션에서 이전에 대상 테이블이 생성되거나 잘린 경우

또한 중요

  • CREATE TABLE AS 의사 유형을 사용할 수 없게 만듭니다. serial 곧장.하지만 이는 단지 "마크로"이므로 모든 작업을 손으로 대신 수행할 수 있습니다.시퀀스를 생성하고 이를 사용하여 생성 id 가치.마지막으로 열 기본값을 설정하고 열이 시퀀스를 소유하도록 만듭니다.관련된:

  • plpgsql 함수 래퍼는 선택 사항입니다(반복 사용에 편리함). 트랜잭션의 일반 SQL: BEGIN; ... COMMIT;

  • 추가 PRIMARY KEY ~ 후에 (기본) 인덱스를 한 조각으로 생성하는 것이 값을 점진적으로 추가하는 것보다 빠르기 때문에 데이터를 삽입하는 것도 더 빠릅니다.

  • 당신은 논리 오류 파티셔닝에서 :

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

    마지막 행은 다음 파티션과 겹치고 행이 반복적으로 삽입되어 PK에서 고유한 위반이 발생합니다.사용 < 대신에 <= 문제를 해결할 것이지만 파티셔닝을 완전히 제거했습니다.

  • 이것을 반복해서 실행하면, 다중 열 인덱스 ~에 digiroad_segmentti (k_elem_id, tyyppi, dyn_tyyppi, region) 데이터 배포에 따라 비용이 지불될 수 있습니다.

사소한 것

  • 언어를 인용하지 마세요 plpgsql 이름은 식별자입니다.
  • 매개변수가 없는 함수를 다음과 같이 표시하는 것은 의미가 없습니다. STRICT.
  • VOLATILE 기본값이며 소음입니다.
  • 사용 COALESCE NULL 값에 대한 기본값을 제공합니다.

  • 당신의 일부 double precision (float8) 열은 다음과 같이 더 잘 작동할 수 있습니다. integer 넌 대부분 그랬으니까 numeric (9,0) 아마도 더 저렴한 일반 테이블로 교체할 수 있는 오래된 테이블에 integer.

  • 칼럼 region varchar(40) 정규화 후보처럼 보입니다(지역이 대부분 고유하지 않은 경우). 지역 테이블을 만들고 다음을 사용하세요. region_id 기본 테이블의 FK 열로.

다른 팁

내용만 변경했다면 shared_buffers,work_mem, 그리고 effective_cache_size 구성 변수를 사용하면 아마도 여전히 실행 중일 것입니다. checkpoint_segments=3.

이 경우에는 WAL 세그먼트가 세 개뿐이므로 지속적으로 재활용해야 하며 매번 데이터 파일에 강제로 쓰기를 수행해야 합니다. 이로 인해 엄청난 양의 I/O 활동이 발생하고 확실히 컴퓨터 속도가 느려질 수 있습니다.로그를 보고 다음 문구를 검색하여 체크포인트 동작을 확인할 수 있습니다. checkpoints are occurring too frequently.또한 활성화하여 그들이 무엇을 하고 있는지 볼 수도 있습니다. log_checkpoints=on postgresql.conf에서

나는 당신을 바꾸는 것이 좋습니다 checkpoint_segments 40처럼 더 큰 것으로, checkpoint_completion_target 설명하는 동작을 시도하고 부드럽게 하려면 0.9로 변경하세요.

설정에 대한 자세한 내용은 9.3용 PostgreSQL 설명서의 미리 쓰기 로그 부분.=)

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 dba.stackexchange
scroll top