ما الذي يسبب إدراج كبير لإبطاء واستخدام القرص لتنفجر?
سؤال
لدي جدول من حوالي 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 في اكتب سجل مسبق القسم.=)