سؤال

لدي جدول من حوالي 3.1 مليون الصفوف مع التعريف والفهارس التالية:

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

يحتوي جدول آخر يحتوي على 8.6 مليون صف على سمات لصفوف الجدول الأول ، ويمكن ضم الجداول مع 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)
);

نظرا لأن تشغيل عبارة إدراج واحدة سيستغرق وقتا طويلا ولن أتمكن من معرفة ما إذا كانت العبارة عالقة أو شيء من هذا القبيل ، فقد قررت القيام بذلك في أجزاء أصغر داخل حلقة في وظيفة.

تبدو الوظيفة كما يلي:

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;

والمشكلة هي أنه يبدأ الذهاب من خلال الحلقات بسرعة كبيرة ، ولكن بعد ذلك في مرحلة ما يبطئ إلى الزحف.عندما يبطئ ، في نفس الوقت استخدام القرص في بلدي ويندوز 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

نظام بلدي يعمل كيو 9.3.5 على ويندوز 8 مع 8 جيجابايت من ذاكرة الوصول العشوائي.

لقد جربت مع أحجام دفعة مختلفة ، والقيام الاستعلام بطرق مختلفة وزيادة متغيرات الذاكرة في تكوين بوستغريس ، ولكن لا شيء يبدو أن حل المشكلة حقا.

متغيرات التكوين التي تم تغييرها من قيمها الافتراضية:

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

أود أن أعرف ما الذي يسبب حدوث ذلك وما الذي يمكن فعله حيال ذلك.

هل كانت مفيدة؟

المحلول

عند إنشاء الجدول الجديد تجنب تكلفة الكتابة أكتب قبل الدخول (WAL) تماما مع CREATE TABLE AS.
انظر @Kassandry الجواب عن تفسير كيف وول الأرقام في هذا.

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;

الوثائق:

وبصرف النظر عن تجنب وقت أرشيفي أو وول المرسل إلى عملية WAL البيانات ، عند القيام بذلك سوف تجعل في الواقع بعض الأوامر بشكل أسرع ، لأنها مصممة على عدم كتابة وول في كل حال wal_level هو minimal.(أنها يمكن أن تضمن سلامة تحطم أكثر بثمن بخس قبل القيام fsync في النهاية من كتابة وول.) وهذا ينطبق على ما يلي الأوامر:

  • 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.

في هذه الحالة لديك فقط ثلاثة قطاعات وول ، وعلى هذا النحو ، تحتاج إلى إعادة تدويرها بشكل مستمر ، مما اضطر يكتب إلى ملفات البيانات في كل مرة ، مما يسبب كمية كبيرة من نشاط الإدخال/الإخراج ويمكن بالتأكيد إبطاء الجهاز الخاص بك إلى الزحف.يمكنك التحقق من سلوك نقاط التفتيش من خلال البحث في السجل والبحث عن العبارة checkpoints are occurring too frequently.يمكنك أيضا إلقاء نظرة على ما يفعلونه من خلال التمكين log_checkpoints=on في كيو الخاص بك.أسيوط

أود أن أوصي بتغيير الخاص بك checkpoint_segments إلى شيء أكبر ، مثل 40 ، و checkpoint_completion_target إلى 0.9 في محاولة للتخفيف من السلوك الذي كنت تصف.

يتم وصف الإعدادات هنا في وثائق كيو ل 9.3 في اكتب سجل مسبق القسم.=)

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى dba.stackexchange
scroll top