การปรับปรุงประสิทธิภาพการอัปเดตเป็นกลุ่มใน PostgreSQL


37

ใช้ PG 9.1 บน Ubuntu 12.04

ขณะนี้เราใช้เวลาถึง 24 ชั่วโมงในการเรียกใช้ชุดคำสั่ง UPDATE จำนวนมากบนฐานข้อมูลซึ่งเป็นรูปแบบ:

UPDATE table
SET field1 = constant1, field2 = constant2, ...
WHERE id = constid

(เราแค่เขียนทับฟิลด์ของวัตถุที่ระบุด้วย ID) ค่ามาจากแหล่งข้อมูลภายนอก (ยังไม่ได้อยู่ในฐานข้อมูลในตาราง)

ตารางมีดัชนีไม่กี่ข้อและไม่มีข้อ จำกัด ของรหัสต่างประเทศ ไม่มีการกระทำใด ๆ จนกว่าจะสิ้นสุด

ใช้เวลา 2 ชั่วโมงในการนำเข้าpg_dumpฐานข้อมูลทั้งหมด ดูเหมือนว่าพื้นฐานนี้เราควรตั้งเป้าหมายอย่างสมเหตุสมผล

ขาดการผลิตโปรแกรมที่กำหนดเองซึ่งสร้างชุดข้อมูลสำหรับ PostgreSQL เพื่อนำเข้าอีกครั้งมีอะไรที่เราสามารถทำได้เพื่อให้ประสิทธิภาพการอัพเดทจำนวนมากใกล้เคียงกับการนำเข้าหรือไม่ (นี่คือพื้นที่ที่เราเชื่อว่าต้นไม้ที่ผสานโครงสร้างบันทึกการจัดการดี แต่เราสงสัยว่ามีอะไรที่เราสามารถทำได้ภายใน PostgreSQL)

ความคิดบางอย่าง:

  • วางดัชนีที่ไม่ใช่ ID ทั้งหมดและสร้างใหม่ในภายหลังหรือไม่
  • การเพิ่ม checkpoint_segments แต่สิ่งนี้ช่วยให้ปริมาณงานในระยะยาวยั่งยืนหรือไม่
  • ใช้เทคนิคที่กล่าวถึงที่นี่ ? (โหลดข้อมูลใหม่เป็นตารางจากนั้น "ผสานใน" ข้อมูลเก่าโดยไม่พบ ID ในข้อมูลใหม่)

โดยทั่วไปมีหลายสิ่งที่ต้องลองและเราไม่แน่ใจว่าสิ่งที่มีประสิทธิภาพมากที่สุดคืออะไรหรือถ้าเรามองข้ามสิ่งอื่น เราจะใช้เวลาทดสอบอีกสองสามวัน แต่เราคิดว่าเราจะถามที่นี่เช่นกัน

ฉันมีการโหลดพร้อมกันบนโต๊ะ แต่มันเป็นแบบอ่านอย่างเดียว


ไม่มีข้อมูลสำคัญในคำถามของคุณ: รุ่น Postgres ของคุณหรือไม่ คุณค่ามาจากไหน? ฟังดูเหมือนไฟล์นอกฐานข้อมูล แต่โปรดอธิบายให้ชัดเจน คุณมีการโหลดพร้อมกันบนตารางเป้าหมายหรือไม่? ถ้าใช่มันคืออะไรกันแน่? หรือคุณสามารถจะปล่อยและสร้างใหม่ได้? ไม่มีกุญแจต่างประเทศก็โอเค - แต่มีวัตถุอื่นอีกเช่นวิว? โปรดแก้ไขคำถามของคุณด้วยข้อมูลที่ขาดหายไป อย่าบีบในความคิดเห็น
Erwin Brandstetter

@ErwinBrandstetter ขอบคุณอัปเดตคำถามของฉัน
ยาง

ฉันคิดว่าคุณตรวจสอบแล้วexplain analyzeว่ามันใช้ดัชนีสำหรับการค้นหาหรือไม่
rogerdpack

คำตอบ:


45

สมมติฐาน

เนื่องจากไม่มีข้อมูลใน Q ฉันจะถือว่า:

  • ข้อมูลของคุณมาจากไฟล์บนเซิร์ฟเวอร์ฐานข้อมูล
  • ข้อมูลจะถูกจัดรูปแบบเหมือนกับCOPYเอาต์พุตโดยมีค่าเฉพาะ idสำหรับแต่ละแถวเพื่อจับคู่กับตารางเป้าหมาย
    ถ้าไม่ให้ฟอร์แมตอย่างถูกต้องก่อนหรือใช้COPYตัวเลือกเพื่อจัดการกับรูปแบบ
  • คุณกำลังอัปเดตทุกแถวในตารางเป้าหมายหรือส่วนใหญ่
  • คุณสามารถที่จะวางและสร้างตารางเป้าหมายใหม่ได้
    นั่นหมายความว่าไม่มีการเข้าถึงพร้อมกัน ลองพิจารณาคำตอบที่เกี่ยวข้องนี้:
  • ไม่มีวัตถุใด ๆ เลยยกเว้นดัชนี

วิธีการแก้

ผมขอแนะนำให้คุณไปด้วยวิธีการที่คล้ายกันตามที่ระบุไว้ในการเชื่อมโยงจากกระสุนที่สามของคุณ ด้วยการเพิ่มประสิทธิภาพที่สำคัญ

หากต้องการสร้างตารางชั่วคราวมีวิธีที่ง่ายกว่าและเร็วกว่า:

CREATE TEMP TABLE tmp_tbl AS SELECT * FROM tbl LIMIT 0;

ขนาดใหญ่เพียงครั้งเดียวUPDATEจากตารางชั่วคราวภายในฐานข้อมูลจะเร็วกว่าการอัปเดตส่วนบุคคลจากนอกฐานข้อมูลด้วยคำสั่งที่หลากหลาย

ในรูปแบบของ PostgreSQL MVCCเป็นUPDATEวิธีการที่จะสร้างรุ่นแถวใหม่และทำเครื่องหมายเดิมเป็นลบ นั่นเป็นเรื่องเกี่ยวกับราคาแพงINSERTและDELETEรวมกัน นอกจากนี้ยังปล่อยให้คุณมี tuples ที่ตายแล้วจำนวนมาก เนื่องจากคุณกำลังอัปเดตตารางทั้งหมดมันจะเร็วขึ้นโดยรวมในการสร้างตารางใหม่และวางตารางเก่า

หากคุณมี RAM เพียงพอให้ตั้งค่าtemp_buffers(สำหรับเซสชันนี้เท่านั้น) สูงพอที่จะเก็บตาราง temp ใน RAM - ก่อนที่คุณจะทำสิ่งอื่น

ในการรับค่าประมาณจำนวน RAM ที่ต้องการให้รันการทดสอบด้วยตัวอย่างขนาดเล็กและใช้ฟังก์ชันขนาดวัตถุ db :

SELECT pg_size_pretty(pg_relation_size('tmp_tbl'));  -- complete size of table
SELECT pg_column_size(t) FROM tmp_tbl t LIMIT 10;  -- size of sample rows

สคริปต์ที่สมบูรณ์

SET temp_buffers = '1GB';        -- example value

CREATE TEMP TABLE tmp_tbl AS SELECT * FROM tbl LIMIT 0;

COPY tmp_tbl FROM '/absolute/path/to/file';

CREATE TABLE tbl_new AS
SELECT t.col1, t.col2, u.field1, u.field2
FROM   tbl     t
JOIN   tmp_tbl u USING (id);

-- Create indexes like in original table
ALTER TABLE tbl_new ADD PRIMARY KEY ...;
CREATE INDEX ... ON tbl_new (...);
CREATE INDEX ... ON tbl_new (...);

-- exclusive lock on tbl for a very brief time window!
DROP TABLE tbl;
ALTER TABLE tbl_new RENAME TO tbl;

DROP TABLE tmp_tbl; -- will also be dropped at end of session automatically

โหลดพร้อมกัน

การดำเนินการที่เกิดขึ้นพร้อมกันบนโต๊ะ (ซึ่งฉันได้ตัดออกในข้อสันนิษฐานในตอนเริ่มต้น) จะรอเมื่อตารางถูกล็อคใกล้ถึงจุดสิ้นสุดและล้มเหลวทันทีที่มีการทำธุรกรรมเนื่องจากมีการแก้ไขชื่อตารางเป็น OID ทันที ตารางใหม่มี OID ที่แตกต่างกัน ตารางยังคงอยู่ แต่การดำเนินการที่เกิดขึ้นพร้อมกันอาจได้รับการยกเว้นและต้องทำซ้ำ รายละเอียดในคำตอบที่เกี่ยวข้องนี้:

อัปเดตเส้นทาง

หากคุณ (ต้อง) ไปที่UPDATEเส้นทางให้ดร็อปดัชนีใด ๆ ที่ไม่จำเป็นในระหว่างการอัพเดตและสร้างใหม่ในภายหลัง มันถูกกว่ามากในการสร้างดัชนีในชิ้นเดียวกว่าที่จะอัปเดตสำหรับทุกแถว สิ่งนี้อาจอนุญาตสำหรับการปรับปรุงที่น่าสนใจ

ผมอธิบายไว้เป็นขั้นตอนที่คล้ายกันโดยใช้UPDATEในคำตอบที่เกี่ยวข้องอย่างใกล้ชิดเกี่ยวกับเรื่องนี้ดังนั้น

 


1
ฉันแค่อัปเดต 20% ของแถวในตารางเป้าหมายไม่ใช่ทั้งหมด แต่ส่วนใหญ่พอที่การผสานน่าจะดีกว่าการอัพเดทแบบสุ่ม
ยาง

1
@AryehLeibTaurog: นั่นไม่ควรจะเกิดขึ้นตั้งแต่ใช้เวลาออกDROP TABLE Access Exclusive Lockทั้งสองวิธีฉันได้แสดงรายการข้อกำหนดเบื้องต้นไว้ที่ด้านบนของคำตอบของฉัน: You can afford to drop and recreate the target table.มันอาจช่วยล็อคตารางเมื่อเริ่มต้นธุรกรรม ฉันขอแนะนำให้คุณเริ่มต้นคำถามใหม่พร้อมรายละเอียดที่เกี่ยวข้องทั้งหมดเกี่ยวกับสถานการณ์ของคุณเพื่อให้เราได้รับข้อมูลที่ดีที่สุด
Erwin Brandstetter

1
@ErwinBrandstetter น่าสนใจ ดูเหมือนว่าจะขึ้นอยู่กับรุ่นของเซิร์ฟเวอร์ ฉันได้ทำซ้ำข้อผิดพลาดใน 8.4 และ 9.1 โดยใช้อะแดปเตอร์ psycopg2และใช้ไคลเอ็นต์ psql ใน 9.3 ไม่มีข้อผิดพลาด ดูความคิดเห็นของฉันในสคริปต์แรก ฉันไม่แน่ใจว่ามีคำถามที่จะโพสต์ที่นี่ แต่มันอาจคุ้มค่าที่จะชักชวนข้อมูลบางอย่างในรายการ postgresql
Aryeh Leib Taurog

1
ฉันเขียนคลาสตัวช่วยอย่างง่ายใน python เพื่อทำให้กระบวนการเป็นอัตโนมัติ
Aryeh Leib Taurog

3
คำตอบที่มีประโยชน์มาก ในรูปแบบที่เปลี่ยนแปลงเล็กน้อยหนึ่งอาจสร้างตารางชั่วคราวที่มีเฉพาะคอลัมน์ที่จะอัปเดตและคอลัมน์อ้างอิงลบคอลัมน์ที่จะอัปเดตจากตารางเดิมจากนั้นรวมตารางโดยใช้CREATE TABLE tbl_new AS SELECT t.*, u.field1, u.field2 from tbl t NATURAL LEFT JOIN tmp_tbl u;การLEFT JOINอนุญาตให้เก็บแถวที่ไม่มีการอัปเดต ของหลักสูตรNATURALสามารถเปลี่ยนเป็นถูกต้องหรือUSING() ON
Skippy le Grand Gourou

2

หากข้อมูลสามารถทำให้พร้อมใช้งานในไฟล์ที่มีโครงสร้างคุณสามารถอ่านได้โดยใช้wrapper ข้อมูลต่างประเทศและทำการผสานบนตารางเป้าหมาย


3
คุณหมายถึงอะไรโดยเฉพาะโดย "รวมในตารางเป้าหมาย"? เหตุใดการใช้ FDW จึงดีกว่าการคัดลอกลงในตารางชั่วคราว (ตามที่แนะนำในหัวข้อย่อยที่สามในคำถามเดิม)
ยาง

"ผสาน" เช่นเดียวกับในคำสั่ง MERGE sql การใช้ FDW ช่วยให้คุณทำเช่นนั้นได้โดยไม่มีขั้นตอนเพิ่มเติมในการคัดลอกข้อมูลลงในตารางชั่วคราว ฉันสมมติว่าคุณไม่ได้แทนที่ชุดข้อมูลทั้งหมดและจะมีข้อมูลจำนวนหนึ่งในไฟล์ที่จะไม่แสดงถึงการเปลี่ยนแปลงจากชุดข้อมูลปัจจุบัน - หากจำนวนเงินมีการเปลี่ยนแปลงอย่างมีนัยสำคัญ เปลี่ยนตารางอาจจะคุ้มค่า
David Aldridge

1
@DavidAldridge: ในขณะที่กำหนดไว้ในมาตรฐาน SQL: 2003 MERGEจะไม่ถูกนำมาใช้ใน PostgreSQL (ยัง) การใช้งานใน RDBMS อื่นนั้นแตกต่างกันเล็กน้อย พิจารณาข้อมูลแท็กสำหรับและMERGE UPSERT
Erwin Brandstetter

@ErwinBrandstetter [glurk] โอ้ใช่แล้ว Well Merge เป็นไอซิ่งบนเค้กที่ฉันคิดเอาไว้จริงๆ การเข้าถึงข้อมูลโดยไม่ต้องนำเข้าสู่ขั้นตอนชั่วคราวเป็นขั้นตอนสำคัญของเทคนิค FDW
David Aldridge
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.