DELETE ช้ามากใน PostgreSQL หรือไม่?


30

ฉันมีฐานข้อมูลบน PostgreSQL 9.2 ที่มีคีมาหลักที่มีประมาณ 70 ตารางและจำนวนตัวแปรของสกีมาต่อไคลเอนต์ที่มีโครงสร้างเหมือนกันจำนวน 30 ตาราง สกีมาไคลเอนต์มีคีย์ต่างประเทศอ้างอิงถึงสกีมาหลักและไม่ใช่วิธีอื่น ๆ

ฉันเพิ่งเริ่มเติมฐานข้อมูลด้วยข้อมูลจริงบางอย่างที่นำมาจากเวอร์ชันก่อนหน้า ฐานข้อมูลมาถึงประมาณ 1.5 GB (คาดว่าจะเพิ่มขึ้นเป็น 10s GB ภายในไม่กี่สัปดาห์) เมื่อฉันต้องทำการลบจำนวนมากในตารางกลางในสคีมาหลัก คีย์ต่างประเทศที่เกี่ยวข้องทั้งหมดจะถูกทำเครื่องหมายว่า DELETE CASCADE

ไม่แปลกใจเลยว่าจะใช้เวลานาน แต่หลังจากผ่านไป 12 ชั่วโมงก็เห็นได้ชัดว่าฉันเริ่มต้นได้ดีกว่าปล่อย DB และเรียกใช้การย้ายข้อมูลอีกครั้ง แต่ถ้าฉันต้องทำซ้ำการดำเนินการนี้ในภายหลังเมื่อฐานข้อมูลมีชีวิตอยู่และมีขนาดใหญ่ขึ้น? มีวิธีอื่นให้เลือกเร็วกว่านี้ไหม?

มันจะเร็วกว่านี้ไหมถ้าฉันเขียนสคริปต์ที่จะเรียกดูตารางที่ขึ้นต่อกันเริ่มต้นที่ตารางที่ไกลที่สุดจากตารางกลางการลบตารางแถวที่อยู่ต่อกันทีละตาราง?

รายละเอียดที่สำคัญคือมีทริกเกอร์ในบางตาราง


4
หลังจาก 5 ปีฉันเปลี่ยนคำตอบที่ยอมรับได้ DELETE ที่ช้ามักเกิดจากการขาดดัชนีในคีย์ต่างประเทศที่อ้างอิงโดยตรงหรือโดยอ้อมตารางที่ถูกลบออก ทริกเกอร์ที่ใช้คำสั่ง DELETE ก็สามารถทำให้สิ่งต่าง ๆ ช้าลงได้เช่นกันแม้ว่าจะมีวิธีแก้ปัญหาเกือบตลอดเวลาเพื่อให้ทำงานได้เร็วขึ้น (เช่นโดยการเพิ่มดัชนีที่ขาดหายไป) และแทบไม่เคยปิดการใช้ทริกเกอร์ทั้งหมด
jd

คำตอบ:


30

ฉันมีปัญหาที่คล้ายกัน เมื่อมันปรากฏออกมาON DELETE CASCADEสิ่งกระตุ้นเหล่านั้นทำให้สิ่งต่าง ๆ ช้าลงเล็กน้อยเนื่องจากการลบแบบเรียงลำดับเหล่านั้นช้ามาก

ฉันแก้ไขปัญหาด้วยการสร้างดัชนีในเขตข้อมูลคีย์ต่างประเทศในตารางอ้างอิงและฉันใช้เวลาหลายชั่วโมงในการลบเป็นเวลาสองสามวินาที


ว้าวสิ่งนี้ช่วยฉันลบ 8M บันทึกในไม่กี่นาที แต่สิ่งที่ฉันไม่เข้าใจคือตารางของฉันมีการอ้างอิงไปยังตารางอื่นเท่านั้นไม่มีตารางอื่น ๆ ที่อ้างอิงกับตารางของฉัน แล้วเอฟเฟกต์ที่นี่คืออะไรกันแน่? (ฉันไม่ได้ใช้ON DELETE CASCADE)
msrd0

2
สิ่งนี้แก้ไขให้ฉันด้วย สำหรับใครก็ตามที่ลองสิ่งนี้คุณสามารถทำEXPLAIN (ANALYZE, BUFFERS)แบบสอบถามในการลบแถวเดียวและควรแสดงให้คุณเห็นว่าข้อ จำกัด ของคีย์ต่างประเทศนั้นใช้เวลานานที่สุด (อย่างน้อยก็ทำเพื่อฉัน)
Justin Workman

ต้องลบแถวที่เรียงซ้อนกัน 600k และตอนเริ่มต้นใช้เวลาระหว่าง 2-10 ต่อการทำงานด้วยการใช้งาน CPU 100% ตอนนี้ใช้เวลาเพียงไม่กี่นาทีในการลบทั้งหมดด้วยการใช้ CPU 80%
fillobotto

สิ่งสำคัญคือให้สังเกตว่าถ้าคุณมีการอ้างอิงต่างประเทศไปที่ใดก็ได้คอลัมน์ซอร์สต้องมีดัชนีจริงหรือประสิทธิภาพจะลดลง ฉันไม่แน่ใจว่าPRIMARYดัชนีเพียงพอหรือไม่ แต่UNIQUEดัชนีนั้นไม่ดีพอสำหรับจุดประสงค์นี้
Mikko Rantalainen

26

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

ALTER TABLE tablename DISABLE TRIGGER ALL; 
DELETE ...; 
ALTER TABLE tablename ENABLE TRIGGER ALL;

คีย์หลักที่นี่คือคุณต้องการลดความลึกของเคียวรีย่อย ในกรณีนี้คุณอาจต้องการตั้งค่าตาราง temp เพื่อจัดเก็บข้อมูลที่เกี่ยวข้องเพื่อให้คุณสามารถหลีกเลี่ยงการสืบค้นย่อยลึก ๆ ในการลบของคุณ


ในกรณีของฉันฉันเริ่มต้นคำสั่งลบจากก่อนที่จะเข้านอนและยังไม่ได้ทำเมื่อฉันกลับไปที่คอมพิวเตอร์ในวันถัดไป CPU 100% ใช้ในหนึ่งคอร์ตลอดเวลา หลังจากปิดการใช้งานทริกเกอร์แล้วลองอีกครั้งใช้เวลา 3 วินาทีในการลบบันทึก 200k ขอขอบคุณ!
Nick Woodhams

13

วิธีที่ง่ายที่สุดในการแก้ปัญหาคือการสอบถามรายละเอียดระยะเวลาจาก EXPLAINPostgreSQL: สำหรับสิ่งนี้คุณต้องค้นหาอย่างน้อยหนึ่งแบบสอบถามที่ไม่สมบูรณ์ แต่ใช้เวลานานกว่าที่คาดไว้ สมมุติว่าเส้นตรงนี้มีหน้าตา

delete from mydata where id='897b4dde-6a0d-4159-91e6-88e84519e6b6';

แทนที่จะใช้คำสั่งนั้นจริงๆคุณสามารถทำได้

begin;
explain (analyze,buffers,timing) delete from mydata where id='897b4dde-6a0d-4159-91e6-88e84519e6b6';
rollback;

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

...
Trigger for constraint XYZ123: time=12311.292 calls=1
...

มีหน่วยtimeเป็นมิลลิวินาที (มิลลิวินาที) ดังนั้นการตรวจสอบข้อ จำกัด นี้ใช้เวลาประมาณ 12.3 วินาที คุณต้องเพิ่มใหม่INDEXผ่านคอลัมน์ที่จำเป็นเพื่อให้สามารถคำนวณทริกเกอร์นี้ได้อย่างมีประสิทธิภาพ สำหรับการอ้างอิงคีย์ต่างประเทศคอลัมน์ที่อ้างอิงไปยังตารางอื่นจะต้องทำดัชนี (นั่นคือคอลัมน์ต้นฉบับไม่ใช่คอลัมน์เป้าหมาย) PostgreSQL ไม่ได้สร้างดัชนีดังกล่าวโดยอัตโนมัติสำหรับคุณและDELETEเป็นคำถามทั่วไปที่คุณต้องการดัชนีนั้นจริงๆ เป็นผลให้คุณอาจสะสมปีของข้อมูลจนกว่าคุณจะตีกรณีที่DELETEช้าเกินไปเนื่องจากขาดดัชนี

เมื่อคุณแก้ไขข้อ จำกัด ดังกล่าวแล้ว (หรือสิ่งอื่น ๆ ที่ใช้เวลานานเกินไป) ให้ทำซ้ำคำสั่งในbegin/ rollbackblock เพื่อให้คุณสามารถเปรียบเทียบเวลาดำเนินการใหม่กับก่อนหน้า ดำเนินการต่อไปจนกว่าคุณจะพอใจกับเวลาตอบกลับการลบบรรทัดเดียว (ฉันได้รับข้อความค้นหาหนึ่งรายการจาก 25.6 วินาทีถึง 15 ms เพียงแค่เพิ่มดัชนีอื่น ๆ ) จากนั้นคุณสามารถดำเนินการลบให้เสร็จสมบูรณ์โดยไม่แฮ็คใด ๆ

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


8

การปิดการใช้งานทริกเกอร์อาจเป็นภัยคุกคามต่อความสมบูรณ์ของฐานข้อมูลและไม่สามารถแนะนำได้ อย่างไรก็ตามหากคุณแน่ใจว่าการดำเนินการของคุณเป็นข้อจำกัดความล้มเหลวคุณสามารถปิดการใช้งานทริกเกอร์ได้ดังนี้:SET session_replication_role = replica;

เรียกใช้ที่DELETEนี่

ในการกู้คืนทริกเกอร์ให้เรียกใช้: SET session_replication_role = DEFAULT;

แหล่งที่มาที่นี่


0

หากคุณมีทริกเกอร์ ON DELETE CASCADE พวกเขาหวังว่าจะมีเหตุผลและไม่ควรปิดใช้งาน เคล็ดลับอื่น (ยังคงเพิ่มดัชนีของคุณ) ที่เหมาะกับฉันก็คือการสร้างฟังก์ชั่นลบที่ลบข้อมูลที่เริ่มต้นด้วยตารางในตอนท้ายของน้ำตกด้วยตนเองและทำงานไปที่ตารางหลัก (นี่คือสิ่งเดียวกับที่คุณจะต้องถ้าคุณมีทริกเกอร์ ON DELETE RESTRICT)

CREATE TABLE tablea (
    tablea_uid integer
);

CREATE TABLE tableb (
    tableb_uid integer,
    tablea_rid integer REFERENCES tablea(tablea_uid)
);

CREATE TABLE tablec (
    tablec_uid integer,
    tableb_rid integer REFERENCES tableb(tableb_uid)
);

ในกรณีนี้ลบข้อมูลใน tablec จากนั้น tableb แล้ว tablea

CREATE OR REPLACE FUNCTION delete_in_order()
 RETURNS void AS $$

    DELETE FROM tablec;
    DELETE FROM tableb;
    DELETE FROM tablea;

$$ LANGUAGE SQL;
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.