เหตุใดฉันจึงต้องทำธุรกรรมในโหมดไฮเบอร์เนตสำหรับการดำเนินการแบบอ่านอย่างเดียว


107

เหตุใดฉันจึงต้องทำธุรกรรมในโหมดไฮเบอร์เนตสำหรับการดำเนินการแบบอ่านอย่างเดียว

ธุรกรรมต่อไปนี้ทำให้เกิดการล็อกในฐานข้อมูลหรือไม่

ตัวอย่างรหัสที่จะดึงข้อมูลจากฐานข้อมูล:

Transaction tx = HibernateUtil.getCurrentSession().beginTransaction(); // why begin transaction?
//readonly operation here

tx.commit() // why tx.commit? I don't want to write anything

ใช้session.close() แทนได้tx.commit()หรือไม่?


9
DB เองต้องการการทำธุรกรรม คุณสามารถอ่านเกี่ยวกับโหมด autocommit ได้ที่นี่: community.jboss.org/wiki/…
Bhesh Gurung

@BheshGurung ฉันเดาว่าเราต้องการการแปลงสำหรับการเขียนเท่านั้น
user93796

4
คุณอ่านส่วน "Debunking auto-commits" ในลิงก์หรือไม่
Bhesh Gurung

คำตอบ:


131

คุณอาจมีเหตุผลในการทำเครื่องหมายธุรกรรมเป็นแบบอ่านอย่างเดียว

  1. ธุรกรรมสำหรับการอ่านอาจดูแปลกและบ่อยครั้งที่ผู้คนไม่ได้ทำเครื่องหมายวิธีการทำธุรกรรมในกรณีนี้ แต่ JDBC จะสร้างธุรกรรมอย่างไรก็ตามมันจะใช้งานได้autocommit=trueหากไม่ได้ตั้งค่าตัวเลือกอื่นไว้อย่างชัดเจน
  2. แต่ไม่มีการรับประกันว่าวิธีการของคุณจะไม่เขียนลงในฐานข้อมูล หากคุณทำเครื่องหมายวิธีการเป็น@Transactional(readonly=true)Spring จะตั้งค่าธุรกรรม JDBC เป็นโหมดอ่านอย่างเดียวดังนั้นคุณจะกำหนดว่าเป็นไปได้จริงหรือไม่ที่จะเขียนลงใน DB ในขอบเขตของธุรกรรมนี้ หากสถาปัตยกรรมของคุณยุ่งยากและสมาชิกในทีมบางคนอาจเลือกที่จะใส่คำค้นหาการปรับเปลี่ยนในที่ที่ไม่คาดคิดแฟล็กนี้จะนำคุณไปยังจุดที่มีปัญหา
  3. นอกจากนี้การทำธุรกรรมแบบอ่านอย่างเดียวยังสามารถปรับให้เหมาะสมโดย DB ได้ แต่แน่นอนว่านี่เป็น DB เฉพาะ เช่น MySQL เพิ่มการรองรับสำหรับสิ่งนี้เฉพาะใน InnoDB โดยเริ่มจากเวอร์ชัน 5.6.4
  4. หากคุณไม่ได้ใช้ JDBC โดยตรง แต่เป็น ORM นั่นอาจเป็นปัญหาได้ ตัวอย่างเช่นชุมชน Hibernate กล่าวว่าการทำงานนอกธุรกรรมอาจทำให้เกิดพฤติกรรมที่คาดเดาไม่ได้ เนื่องจากไฮเบอร์เนตจะเปิดธุรกรรม แต่จะไม่ปิดด้วยตัวเองดังนั้นการเชื่อมต่อจะถูกส่งกลับไปยังพูลการเชื่อมต่อโดยไม่มีการทำธุรกรรม แล้วจะเกิดอะไรขึ้น? JDBC ยังคงเงียบดังนั้นนี่คือการใช้งานเฉพาะ (MySQL ย้อนกลับธุรกรรม Oracle ก็ยอมรับ) นอกจากนี้ยังสามารถกำหนดค่าในระดับพูลการเชื่อมต่อ (เช่น C3P0 จะให้ตัวเลือกดังกล่าวย้อนกลับโดยค่าเริ่มต้น)
  5. อีกประการหนึ่งเมื่อพูดถึงไฮเบอร์เนต Spring ตั้งค่า FlushMode เป็น MANUAL ในกรณีของการทำธุรกรรมแบบอ่านอย่างเดียวซึ่งนำไปสู่การเพิ่มประสิทธิภาพอื่น ๆ เช่นไม่จำเป็นต้องตรวจสอบสกปรก
  6. คุณอาจต้องการลบล้างหรือตั้งค่าระดับการแยกธุรกรรมอย่างชัดเจน สิ่งนี้ส่งผลกระทบต่อธุรกรรมการอ่านเช่นกันเนื่องจากคุณทำหรือไม่ต้องการอ่านการเปลี่ยนแปลงที่ไม่ถูกผูกมัดเปิดรับการอ่านแบบหลอน ฯลฯ

สรุป - คุณสามารถทำได้ทั้งสองวิธี แต่คุณต้องเข้าใจผลที่ตามมา


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

แฟล็กแบบอ่านอย่างเดียวถูกตั้งค่าไว้สำหรับการเชื่อมต่อไม่ใช่สำหรับการทำธุรกรรมเองและคุณไม่สามารถเข้าถึงผ่านไฮเบอร์เนตได้เช่นนั้น คุณต้องใช้การสนับสนุน Spring Transaction และใช้คำอธิบายประกอบหรือการกำหนดค่าธุรกรรมตาม XML การทำเช่นนั้น Spring จะเป็นเจ้าของการเชื่อมต่อและการจัดการธุรกรรมแทน Hibernate
Stanislav Bashkyrtsev

1
อธิบายได้ดีมาก! บทความจากมาร์คริชาร์ดอีกอธิบายสวยดีผิดพลาดของธงอ่านอย่างเดียวในการทำธุรกรรมคำอธิบายประกอบ - ibm.com/developerworks/java/library/j-ts1/index.html
Mahesh

48

คำสั่งฐานข้อมูลทั้งหมดดำเนินการภายในบริบทของธุรกรรมทางกายภาพแม้ว่าเราจะไม่ได้ประกาศขอบเขตธุรกรรมอย่างชัดเจน (BEGIN / COMMIT / ROLLBACK)

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

การประกาศบริการตามที่@Transactionalจะทำให้คุณมีการเชื่อมต่อหนึ่งรายการสำหรับระยะเวลาการทำธุรกรรมทั้งหมดและคำสั่งทั้งหมดจะใช้การเชื่อมต่อแบบแยกเดี่ยวนั้น นี่เป็นวิธีที่ดีกว่าการไม่ใช้ธุรกรรมที่ชัดเจนตั้งแต่แรก

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

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


1
"... จากนั้นแต่ละคำสั่งจะต้องดำเนินการแยกกัน" คอนเทนเนอร์ไม่ทำการประมวลผลแบบเป็นชุดโดยอัตโนมัติ?
Arash

การประมวลผลแบบกลุ่มมีไว้สำหรับการแก้ไขไม่ใช่สำหรับการอ่านข้อมูล และถึงกระนั้นการทำแบตช์ JDBCก็ไม่ได้เปิดใช้งานตามค่าเริ่มต้นเมื่อใช้ JPA และ Hibernate
Vlad Mihalcea

1
ขอบคุณวลาด ฉันอ่านบทความของคุณบางส่วน พวกเขามีประโยชน์จริงๆ
Arash

stackoverflow.com/q/34797480/179850นี้ดูเหมือนจะขัดแย้งกับคำยืนยันของคุณเล็กน้อยว่าการทำธุรกรรมแบบอ่านอย่างเดียวมีประโยชน์ด้านประสิทธิภาพที่ยอดเยี่ยม อย่างน้อยก็ดูเหมือนจะไม่เป็นเช่นนั้นในบริบทของการจำศีล
nimai

ไม่มันไม่ได้ นั่นเกี่ยวกับการตั้งค่าแบบอ่านอย่างเดียวในขณะที่ของฉันเกี่ยวกับการหลีกเลี่ยงการคอมมิตอัตโนมัติ
Vlad Mihalcea

18

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

ตอนนี้ถ้าคุณโชคดีและการสอบถามของคุณกับ DB นั้นทำให้ ORM แมปสิ่งเหล่านี้กับการสืบค้น SQL เดียวเสมอคุณสามารถออกไปได้โดยไม่ต้องทำธุรกรรมอย่างชัดเจนโดยอาศัยพฤติกรรมการสื่อสารอัตโนมัติในตัวของ DB แต่ ORM เป็นระบบที่ค่อนข้างซับซ้อน ดังนั้นจึงไม่ปลอดภัยเลยที่จะพึ่งพาพฤติกรรมดังกล่าวเว้นแต่คุณจะพยายามทำงานมากขึ้นเพื่อตรวจสอบว่าการใช้งานนั้นทำอะไรได้จริง การเขียนขอบเขตธุรกรรมที่ชัดเจนในนั้นง่ายกว่ามากที่จะทำให้ถูกต้อง (โดยเฉพาะอย่างยิ่งถ้าคุณสามารถทำได้ด้วย AOP หรือเทคนิคที่ขับเคลื่อนด้วย ORM ที่คล้ายกันฉันคิดว่าจาก Java 7 เป็นต้นไปลองใช้กับทรัพยากรได้เช่นกัน)


14

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

ฉันเคยเห็นโปรแกรมที่ผิดพลาดในการฆ่าระบบฐานข้อมูลขนาดใหญ่เนื่องจากเพิ่งอ่านข้อมูล แต่ไม่เคยกระทำการบังคับให้บันทึกธุรกรรมเติบโตเนื่องจาก DB ไม่สามารถปล่อยข้อมูลธุรกรรมก่อน COMMIT หรือ ROLLBACK แม้ว่าไคลเอ็นต์จะไม่ทำอะไรเลยก็ตาม เป็นเวลาหลายชั่วโมง

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