การอัพเดตแถวด้วยค่าเดียวกันอัพเดตแถวจริงหรือไม่?


28

ฉันมีคำถามเกี่ยวกับประสิทธิภาพ สมมติว่าฉันมีผู้ใช้ชื่อ Michael ใช้แบบสอบถามต่อไปนี้:

UPDATE users
SET first_name = 'Michael'
WHERE users.id = 123

แบบสอบถามจะดำเนินการอัปเดตจริงหรือไม่แม้ว่าจะมีการอัปเดตเป็นค่าเดียวกันหรือไม่ ถ้าเป็นเช่นนั้นฉันจะป้องกันไม่ให้เกิดขึ้นได้อย่างไร


1
ทำไมคุณต้องดำเนินการคำสั่งและคาดว่าจะไม่ดำเนินการพร้อมกัน
Max Vernon

@ MaxVernon Ruby on Rails 'ORM ไม่อัปเดตบันทึกดังนั้นฉันอยากรู้ว่า PostgreSQL ทำสิ่งเดียวกัน
OneSneakyMofo

1
ฉันอยากจะแนะนำว่า Ruby on Rails ทำอย่างนั้นหรือเปล่าอาจจะทำการเลือกก่อนเพื่อดูว่าแถวนั้นต้องการการอัพเดทหรือไม่
Max Vernon

คำตอบ:


35

เนื่องจากโมเดล MVCCของ Postgres และตามกฎของ SQL การUPDATEเขียนเวอร์ชันแถวใหม่สำหรับทุกแถวที่ไม่รวมอยู่ในส่วนWHEREคำสั่ง

นี้ไม่ได้มีผลกระทบอย่างมีนัยสำคัญมากขึ้นหรือน้อยลงในการปฏิบัติงานโดยตรงและโดยอ้อม "การอัปเดตที่ว่างเปล่า" มีค่าใช้จ่ายต่อแถวเหมือนกับการอัปเดตอื่น ๆ พวกเขาเรียกใช้ทริกเกอร์ (หากมี) เช่นอัพเดตอื่น ๆ พวกเขาจะต้องเข้าสู่ระบบ WALและพวกเขาผลิตแถวที่ตายแล้วจะพองตัวในตารางและทำให้ทำงานได้มากขึ้นในVACUUMภายหลังเช่นอัพเดตอื่น ๆ

รายการดัชนีและคอลัมน์TOASTที่ไม่มีคอลัมน์ใด ๆ ที่เกี่ยวข้องสามารถเปลี่ยนแปลงได้เหมือนเดิม แต่จะเป็นจริงสำหรับแถวที่อัพเดต ที่เกี่ยวข้อง:

เป็นความคิดที่ดีที่จะยกเว้นการอัปเดตที่ว่างเปล่าดังกล่าวอยู่เสมอ (เมื่อมีโอกาสจริงที่อาจเกิดขึ้น) คุณไม่ได้ให้คำจำกัดความของตารางในคำถามของคุณ (ซึ่งเป็นความคิดที่ดีเสมอ) เราต้องสมมติว่าfirst_nameสามารถเป็น NULL ได้ (ซึ่งไม่น่าแปลกใจสำหรับ "ชื่อ") ดังนั้นการสืบค้นจึงต้องใช้การเปรียบเทียบแบบ NULL-safe :

UPDATE users
SET    first_name = 'Michael'
WHERE  id = 123
AND   first_name IS DISTINCT FROM 'Michael';

ถ้าfirst_name IS NULLก่อนการอัพเดทการทดสอบที่เพิ่งfirst_name <> 'Michael'จะประเมินค่าเป็น NULL และเช่นนั้นไม่รวมแถวจากการอัพเดต ข้อผิดพลาดส่อเสียด หากมีการกำหนดNOT NULLคอลัมน์ให้ใช้การตรวจสอบความเท่าเทียมกันอย่างง่าย ๆ เนื่องจากมีราคาถูกกว่าเล็กน้อย

ที่เกี่ยวข้อง:


1
Indexes entries and TOASTed columns where none of the involved columns are changed can stay the sameแต่พวกเขาจะไม่ได้รับการปรับปรุงให้ชี้ไปที่ตำแหน่งใหม่ของแถวหรือไม่
dvtan

1
@dtgq: ไม่ได้มีการอัปเดต HOT ซึ่งดัชนีสามารถชี้ไปยังตำแหน่งเดิมได้เรื่อย ๆ และการดึงข้อมูลฮีปต้องผ่านการเชื่อมโยงห่วงโซ่ HOT เพื่อรับ tuple สด ฉันเพิ่มลิงก์ไปยังคำอธิบายเพิ่มเติมด้านบน
Erwin Brandstetter

1
สิ่งที่เกี่ยวกับ MVCC เรียกร้องให้มีการปรับปรุง noop เพื่อเขียน tuple ใหม่
jberryman

@jberryman: ไม่แน่ใจว่าฉันเข้าใจ ทั้งสองวิธีโปรดถามคำถามใหม่คำถาม คุณสามารถเชื่อมโยงกับบริบทนี้ได้ตลอดเวลา และคุณสามารถแสดงความคิดเห็นที่นี่เพื่อเชื่อมโยงกลับ (และได้รับความสนใจของฉัน)
Erwin Brandstetter

2
@jberryman: ฉันไม่รู้จริง ๆว่าทำไมโครงการถึงเป็นแบบนี้ ที่ก่อตั้งมานานแล้ว แต่ฉันคิดว่ามันคงไม่จำเป็นที่จะต้องตรวจสอบความเท่าเทียมกันทุกแถวและมีรหัสเส้นทางแยกต่างหากสำหรับแถวที่ไม่มีการเปลี่ยนแปลง การจัดการ transaction-ID จะซับซ้อนกว่า - เคสพิเศษสำหรับการrollbackจัดการสแน็ปช็อตการจัดการล็อค WAL สิ่งที่ไม่ ...
Erwin Brandstetter

4

คำสั่ง ORM ที่คล้ายกับ Ruby on Rail ของการดำเนินการรอการตัดบัญชีซึ่งทำเครื่องหมายบันทึกว่ามีการเปลี่ยนแปลง (หรือไม่) จากนั้นเมื่อจำเป็นหรือถูกเรียกใช้จากนั้นส่งการเปลี่ยนแปลงไปยังฐานข้อมูล

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

ดังนั้นจึงจะอัปเดตค่าโดยไม่คำนึงว่าเป็นค่าใหม่หรือไม่

หากคุณต้องการป้องกันสิ่งนี้คุณสามารถใช้รหัสเช่น Max Vernon ที่แนะนำในคำตอบของเขา


2

คุณสามารถเพิ่มไปยังwhereข้อ:

UPDATE users
SET first_name = 'Michael'
WHERE users.id = 123
    AND (first_name <> 'Michael' OR first_name IS NULL);

หากfirst_nameมีการกำหนดเป็นNOT NULLที่OR first_name IS NULLส่วนที่สามารถถอดออกได้

เงื่อนไข:

(first_name <> 'Michael' OR first_name IS NULL)

นอกจากนี้ยังสามารถเขียนได้อย่างหรูหรามากขึ้นเป็น (ในคำตอบของเออร์วิน):

first_name IS DISTINCT FROM 'Michael'

ไม่ทราบว่าคอลัมน์สามารถเป็น NULL ได้หรือไม่ซึ่งอาจทำให้เกิดข้อผิดพลาดส่อเสียด
Erwin Brandstetter

1
@ErwinBrandstetter ฉันได้รับคำตอบแล้ว - ฉันเห็นความคิดเห็นและคำตอบของคุณ!
ypercubeᵀᴹ

ขอบคุณสำหรับการแก้ไข @ypercube - และสำหรับความคิดเห็นเกี่ยวกับNULL@erwin
Max Vernon

1

จากมุมมองฐานข้อมูล

คำตอบสำหรับคำถามของคุณคือใช่ การอัปเดตจะเกิดขึ้น ฐานข้อมูลไม่ได้ตรวจสอบค่าก่อนหน้ามันจะตั้งค่าใหม่เท่านั้น

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

จากมุมมองของ ORM

โดยปกติคุณจะมี Object ที่แสดงถึงแถวเดียวของฐานข้อมูล (มันอาจซับซ้อนกว่านั้นมาก แต่ให้มันง่าย) วัตถุนี้ได้รับการจัดการในหน่วยความจำ (ที่ระดับเซิร์ฟเวอร์แอป) และเฉพาะวัตถุที่ได้รับการยอมรับล่าสุดเท่านั้นที่จะนำไปยังฐานข้อมูล ณ จุดหนึ่ง

นั่นอาจอธิบายพฤติกรรมที่แตกต่าง

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

สนุก!

ฉันหวังว่านี่จะอธิบายแนวคิดบางอย่าง


4
ผลการดำเนินงานเป็นและปัญหา การอัปเดตทุกครั้งจะต้องเขียนลงดิสก์ (บันทึกและตาราง)
ypercubeᵀᴹ

มันจะขึ้นอยู่กับ RDBMS จริงที่คุณใช้ แต่ส่วนใหญ่ไม่ได้คอมมิตทุกครั้ง แต่มีเฉพาะบล็อกที่ถูกคอมมิตล่าสุดที่พวกเขามีอยู่ในหน่วยความจำ คุณไม่เคยอ่านหรือเขียนแถวเดียวในฐานข้อมูล คุณอ่าน / เขียนบล็อกและเก็บไว้ในหน่วยความจำจนกว่าคุณจะต้องล้างออกเพื่อใส่บล็อกใหม่ในที่เดียวกัน ในขณะที่อยู่ในหน่วยความจำการเปลี่ยนแปลงในแถวทุกครั้งจะไม่ถูกเขียนไปยังดิสก์ แต่เฉพาะเนื้อหาบล็อกเมื่อกระบวนการ "ตัวเขียนฐานข้อมูล" ถูกส่งสัญญาณเพื่อถ่ายโอนข้อมูลบล็อกหน่วยความจำนั้นลงใน datafile ดังนั้นไม่ใช่ ... ไม่ใช่ปัญหาหากแอปพลิเคชันของคุณถือบล็อกที่ไม่ได้รับการยอมรับนานเกินไป
Silvarion

1
คำถามนั้นเกี่ยวกับ Postgres ไม่ใช่เกี่ยวกับ DBMS ใด ๆ และในขณะที่การอัพเดตไม่จำเป็นต้องเขียนทีละตัวการเขียนบนฐานข้อมูลจะต้องถูกเขียนลงในบันทึก หากการเปลี่ยนแปลงไม่ได้ถูกเขียนลงในที่จัดเก็บข้อมูลถาวร DBMS จะอยู่รอดได้อย่างไรระบบล่ม
ypercubeᵀᴹ

ใช่มันเขียนในบันทึกจากหน่วยความจำเช่นกันในระหว่างจุดตรวจ นอกจากว่าคุณจะมีผู้ใช้งานพร้อมกันจำนวนมากอย่างมากมันก็ไม่น่าจะมีปัญหาอะไร บันทึกจะถูกเขียนเป็นชุดเช่นกัน ฉันคิดว่าเรากำลังพูดถึงเซิร์ฟเวอร์ หากคุณกำลังพูดถึงฐานข้อมูล Postgres ในแล็ปท็อปที่มี HDD 5400RPM ใช่คุณจะมีปัญหาเรื่องประสิทธิภาพอยู่เสมอ ดังนั้นคำตอบสุดท้ายจะเป็นคำตอบแรก ... มันขึ้นอยู่กับหลายสิ่งหลายอย่างเกินไป
Silvarion
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.