วิธีแก้ไขการขาดธุรกรรมใน MongoDB?


139

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

เราต้องการทดสอบนำร่อง MongoDB ที่ บริษัท ของเรา เราเลือกโครงการง่ายๆ - เกตเวย์ SMS ช่วยให้ซอฟต์แวร์ของเราส่งข้อความ SMS ไปยังเครือข่ายเซลลูลาร์และเกตเวย์ทำงานได้ไม่ดี: สื่อสารกับผู้ให้บริการผ่านโปรโตคอลการสื่อสารที่แตกต่างกัน เกตเวย์ยังจัดการการเรียกเก็บเงินของข้อความด้วย ลูกค้าทุกคนที่สมัครใช้บริการจะต้องซื้อเครดิตบ้าง ระบบจะลดยอดคงเหลือของผู้ใช้โดยอัตโนมัติเมื่อมีการส่งข้อความและปฏิเสธการเข้าถึงหากยอดเงินไม่เพียงพอ และเนื่องจากเราเป็นลูกค้าของผู้ให้บริการ SMS บุคคลที่สามเราจึงอาจมียอดคงเหลือเป็นของตนเอง เราต้องติดตามสิ่งเหล่านั้นด้วย

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

ลองนึกภาพงานส่ง SMS ด้วยขั้นตอนต่อไปนี้ในระบบที่เรียบง่ายนี้:

  1. ตรวจสอบว่าผู้ใช้มียอดคงเหลือเพียงพอหรือไม่ ปฏิเสธการเข้าถึงหากมีเครดิตไม่เพียงพอ

  2. ส่งและจัดเก็บข้อความในการรวบรวม SMS พร้อมรายละเอียดและค่าใช้จ่าย (ในระบบถ่ายทอดสดข้อความจะมีstatusคุณสมบัติและงานจะรับส่งและกำหนดราคาของ SMS ตามสถานะปัจจุบัน)

  3. ลดยอดคงเหลือของผู้ใช้โดยค่าใช้จ่ายของข้อความที่ส่ง

  4. บันทึกธุรกรรมในการรวบรวมทรานแซคชัน

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

ฉันคิดสองแนวคิด:

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

  • สร้างหนึ่งคอลเลกชันสำหรับผู้ใช้และอีกหนึ่งสำหรับการทำธุรกรรม การทำธุรกรรมมีสองประเภท: การซื้อเครดิตที่มีการเปลี่ยนแปลงยอดคงเหลือเป็นบวกและข้อความที่ส่งพร้อมการเปลี่ยนแปลงยอดคงเหลือติดลบ ธุรกรรมอาจมีเอกสารย่อย ตัวอย่างเช่นในข้อความที่ส่งรายละเอียดของ SMS สามารถฝังในการทำธุรกรรม ข้อเสีย: ฉันไม่เก็บยอดคงเหลือผู้ใช้ปัจจุบันดังนั้นฉันต้องคำนวณทุกครั้งที่ผู้ใช้พยายามส่งข้อความเพื่อบอกว่าข้อความสามารถผ่านได้หรือไม่ ฉันเกรงว่าการคำนวณนี้จะช้าลงเมื่อจำนวนธุรกรรมที่เก็บไว้เพิ่มขึ้น

ฉันสับสนเล็กน้อยเกี่ยวกับวิธีการเลือก มีวิธีแก้ไขปัญหาอื่น ๆ อีกไหม? ฉันไม่พบแนวทางปฏิบัติที่ดีที่สุดทางออนไลน์เกี่ยวกับวิธีแก้ไขปัญหาประเภทนี้ ฉันเดาว่าโปรแกรมเมอร์หลายคนที่พยายามทำความคุ้นเคยกับโลก NoSQL กำลังเผชิญกับปัญหาที่คล้ายคลึงกันในตอนแรก


61
ยกโทษให้ฉันถ้าฉันผิด แต่ดูเหมือนว่าโครงการนี้จะใช้ที่เก็บข้อมูล NoSQL โดยไม่คำนึงว่าจะได้รับประโยชน์จากมันหรือไม่ NoSQL นั้นไม่ใช่ทางเลือกสำหรับ SQL ในฐานะทางเลือก "แฟชั่น" แต่เมื่อเทคโนโลยี RDBMS เชิงสัมพันธ์ไม่เหมาะกับพื้นที่ปัญหาและที่ไม่ใช่ที่เก็บข้อมูลสัมพันธ์ คำถามของคุณมากมายมี "ถ้ามันเป็น SQL แล้ว ... " & นั่นเตือนให้ฉันระฆัง ทั้งหมดของ NoSQL นั้นมาจากความต้องการในการแก้ปัญหาที่ SQL ไม่สามารถทำได้และพวกมันก็ถูกทำให้เป็นเรื่องทั่วไปเพื่อให้ง่ายต่อการใช้งานและแน่นอนว่า bandwagon เริ่มกลิ้ง
PurplePilot

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

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

9
ฉันเข้าใจฉันควรใช้เครื่องมือที่เหมาะสมสำหรับงาน อย่างไรก็ตามสำหรับฉัน - เมื่อฉันอ่านคำตอบเช่นนี้ดูเหมือนว่า NoSQL จะไม่ดีสำหรับทุกสิ่งที่ข้อมูลมีความสำคัญ เป็นสิ่งที่ดีสำหรับ Facebook หรือ Twitter ที่หากความคิดเห็นหายไปจากโลกใบนี้ แต่สิ่งที่เหนือกว่านั้นไม่ได้ทำธุรกิจ หากเป็นเรื่องจริงฉันไม่เข้าใจว่าทำไมคนอื่นถึงสนใจสร้างเช่น เว็บสโตร์ที่มี MongoDB: kylebanker.com/blog/2010/04/30/mongodb-and-ecommerceแม้จะกล่าวว่าธุรกรรมส่วนใหญ่สามารถเอาชนะได้ด้วยการปฏิบัติการปรมาณู สิ่งที่ฉันกำลังค้นหาคืออะไร
NagyI

2
คุณพูดว่า "ดูเหมือนว่า NoSQL ไม่ดีสำหรับทุกสิ่งที่ข้อมูลมีความสำคัญ" ไม่เป็นความจริงที่ไม่ดี (อาจ) คือการทำธุรกรรมประเภท ACID ของทรานแซคชันของทรานแซคชัน นอกจากนี้ NoSQL ยังได้รับการออกแบบมาสำหรับร้านค้าข้อมูลแบบกระจายซึ่งร้านค้าประเภท SQL สามารถทำได้ยากมากเมื่อคุณเข้าสู่สถานการณ์จำลองการจำลองทาสทาส NoSQL มีกลยุทธ์เพื่อความมั่นคงในที่สุดและทำให้มั่นใจว่าจะใช้เฉพาะชุดข้อมูลล่าสุด แต่ไม่ใช่กรด
PurplePilot

คำตอบ:


23

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

ดูรายละเอียดเพิ่มเติมที่นี่

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


1
ธุรกรรมได้มาถึงแล้ว! 4.0 GA'ed mongodb.com/blog/post/…
Grigori Melnik

ธุรกรรม MongoDB ยังมีข้อ จำกัด เกี่ยวกับขนาดของธุรกรรม 16 MB เมื่อเร็ว ๆ นี้ฉันมีกรณีการใช้งานที่ฉันต้องใส่บันทึก 50k จากไฟล์ลงใน mongoDB ดังนั้นเพื่อรักษาคุณสมบัติอะตอมฉันคิดว่าใช้ธุรกรรม แต่ตั้งแต่ 50k json บันทึก เกินขีด จำกัด นี้จะมีข้อผิดพลาด "ขนาดรวมของการดำเนินธุรกรรมทั้งหมดต้องน้อยกว่า 16793600 ขนาดจริงคือ 16793817" สำหรับรายละเอียดเพิ่มเติมคุณสามารถเข้าชมตั๋วจิรา
Gautam Malik

MongoDB 4.2 (ปัจจุบันเป็นเบต้า RC4) รองรับธุรกรรมขนาดใหญ่ ด้วยการเป็นตัวแทนของการทำธุรกรรมข้ามหลายรายการใน oplog คุณจะสามารถเขียนข้อมูลมากกว่า 16MB ในธุรกรรม ACID เดียว (ขึ้นอยู่กับเวลาดำเนินการสูงสุดเริ่มต้น 60 วินาทีที่มีอยู่) คุณสามารถลองใช้ได้ทันที
Grigori Melnik

MongoDB 4.2 ปัจจุบันเป็น GA ที่รองรับการทำธุรกรรมแบบกระจายเต็มรูปแบบ mongodb.com/blog/post/…
Grigori Melnik

83

อาศัยอยู่โดยไม่มีธุรกรรม

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

ดังนั้นวิธีการของเราที่เราสามารถใช้ในMongoDBการเอาชนะการขาดการทำธุรกรรมคืออะไร?

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

Update, findAndModify, $addToSet(ภายในการปรับปรุง) และ$push(ภายในการปรับปรุง) การดำเนินการดำเนินอะตอมภายในเอกสารฉบับเดียว


2
ฉันชอบวิธีที่คำตอบนี้ทำแทนที่จะตั้งคำถามถ้าเราควรกลับไปที่ฐานข้อมูลเชิงสัมพันธ์ ขอบคุณ @xameeramir!
DonnyTian

3
ส่วนที่สำคัญของรหัสจะไม่ทำงานหากคุณมีเซิร์ฟเวอร์มากกว่า 1 เครื่องต้องใช้บริการล็อคภายนอกแบบกระจาย
Alexander Mills

@AlexanderMills คุณช่วยอธิบายได้มั้ย
Zameer

คำตอบน่าจะเป็นวิดีโอการถอดความจากที่นี่: youtube.com/watch?v=_Iz5xLZr8Lw
Fritz

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

24

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


@Giovanni Bitliner Tokutek นั้นถูกซื้อโดย Percona และในลิงก์ที่คุณให้ฉันไม่เห็นว่ามีการอ้างอิงถึงข้อมูลใด ๆ ที่เกิดขึ้นตั้งแต่การโพสต์ คุณรู้ไหมว่าเกิดอะไรขึ้นกับความพยายามของพวกเขา ฉันส่งที่อยู่อีเมลในหน้านั้นเพื่อหาคำตอบ
Tyler Collier

คุณต้องการอะไรเป็นพิเศษ หากคุณต้องการเทคโนโลยี toku ที่นำไปใช้กับ Mongodb ลองgithub.com/Tokutek/mongoหากคุณต้องการรุ่น mysql พวกเขาอาจเพิ่มมันเข้าไปใน Mysql รุ่นมาตรฐานที่พวกเขามักจะให้บริการ
Giovanni Bitliner

ฉันจะรวม tokutek กับ nodejs ได้อย่างไร
Manoj Sanjeewa

11

นำไปสู่ประเด็น: ถ้าความสมบูรณ์ของธุรกรรมเป็นสิ่งที่ต้องทำอย่าใช้ MongoDB แต่ใช้เฉพาะส่วนประกอบในระบบที่รองรับธุรกรรม มันยากมากที่จะสร้างบางสิ่งบางอย่างบนส่วนประกอบเพื่อให้มีฟังก์ชั่นคล้ายกับ ACID สำหรับส่วนประกอบที่ไม่ได้เป็นไปตามข้อกำหนดของกรด ขึ้นอยู่กับแต่ละ usecases มันอาจทำให้รู้สึกถึงการแยกการกระทำในการทำธุรกรรมและการกระทำที่ไม่ใช่การทำธุรกรรมในบางวิธี ...


1
ฉันเดาว่าหมายความว่า NoSQL สามารถใช้เป็นฐานข้อมูลเพื่อนสนิทกับ RDBMS แบบคลาสสิก ฉันไม่ชอบแนวคิดที่จะผสม NoSQL และ SQL ในโครงการเดียวกัน มันเพิ่มความซับซ้อนและอาจนำเสนอปัญหาที่ไม่สำคัญเช่นกัน
NagyI

1
โซลูชัน NoSQL ไม่ค่อยได้ใช้เพียงอย่างเดียว ร้านขายเอกสาร (Mongo and Couch) อาจเป็นเพียงข้อยกเว้นเดียวจากกฎนี้
Karoly Horvath

7

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

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

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


ขอบคุณสำหรับคำตอบที่ยอดเยี่ยมนี้! ฉันรู้ว่าถ้าฉันใช้การจัดเก็บข้อมูลที่มีความสามารถในการทำธุรกรรมอาจได้รับความเสียหายเนื่องจากระบบ SMS ซึ่งฉันไม่สามารถควบคุมได้ อย่างไรก็ตามด้วย Mongo มีโอกาสที่ความผิดพลาดของข้อมูลอาจเกิดขึ้นภายใน บริษัท เช่นกัน สมมติว่ารหัสเปลี่ยนยอดเงินของผู้ใช้ด้วย findAndModify ยอดคงเหลือจะเป็นค่าลบ แต่ก่อนที่ฉันจะสามารถแก้ไขข้อผิดพลาดที่เกิดข้อผิดพลาดได้และแอปพลิเคชันต้องเริ่มต้นใหม่ ฉันคิดว่าคุณหมายถึงฉันควรใช้สิ่งที่คล้ายกับการมอบหมายสองเฟสตามการรวบรวมธุรกรรมและทำการตรวจสอบการแก้ไขฐานข้อมูลเป็นประจำ
NagyI

9
ไม่เป็นความจริงร้านค้าธุรกรรมจะย้อนกลับหากคุณไม่กระทำการขั้นสุดท้าย
Karoly Horvath

9
นอกจากนี้คุณไม่ได้ส่ง SMS จากนั้นเข้าสู่ DB นั่นเป็นเพียงความผิดปกติ ก่อนอื่นให้เก็บทุกอย่างใน DB และดำเนินการขั้นสุดท้ายจากนั้นคุณสามารถส่งข้อความ ณ จุดนี้บางสิ่งอาจยังคงล้มเหลวดังนั้นคุณต้องมีงาน cron เพื่อตรวจสอบว่าข้อความถูกส่งจริงถ้าไม่ลองส่ง บางทีคิวข้อความเฉพาะจะดีกว่าสำหรับสิ่งนี้ แต่สิ่งที่ทั้งเดือดลงไปไม่ว่าคุณจะสามารถส่ง SMSes ในวิธีการทำธุรกรรม ...
Karoly Horvath

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

ฉันเห็น. นี่ไม่ใช่ข้อ จำกัด ที่ง่ายจริงๆ บางทีฉันควรเรียนรู้เพิ่มเติมเกี่ยวกับวิธีที่ระบบ RDBMS ทำธุรกรรม คุณช่วยแนะนำเนื้อหาออนไลน์หรือหนังสือบางเล่มที่ฉันสามารถอ่านเกี่ยวกับหนังสือเหล่านี้ได้หรือไม่?
NagyI

6

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

ดังนั้นถ้าคุณต้องการเป็นโครงการนำร่องสำหรับ MongoDB จริงๆเลือกหนึ่งที่ง่ายในการที่เคารพ


ขอบคุณที่อธิบาย เสียใจที่ได้ยินเช่นนั้น ฉันชอบความเรียบง่ายของ NoSQL และการใช้ JSON เรากำลังค้นหาทางเลือกในการออม แต่ดูเหมือนว่าเราจะต้องติดกับมันในขณะที่
NagyI

คุณสามารถให้เหตุผลที่ดีว่าทำไม MongoDB ถึงดีกว่า SQL สำหรับงานนี้หรือไม่? โครงการนำร่องฟังดูงี่เง่า
Karoly Horvath

ฉันไม่ได้บอกว่า MongoDB นั้นดีกว่า SQL เราแค่อยากรู้ว่ามันดีกว่า SQL + ORM หรือเปล่า แต่ตอนนี้มันชัดเจนว่าพวกเขาไม่สามารถแข่งขันในโครงการประเภทนี้ได้
NagyI

6

ธุรกรรมไม่อยู่ใน MongoDB ด้วยเหตุผลที่ถูกต้อง นี่เป็นหนึ่งในสิ่งที่ทำให้ MongoDB เร็วขึ้น

ในกรณีของคุณหากการทำธุรกรรมเป็นสิ่งที่จำเป็นมองโกลดูเหมือนจะไม่เหมาะสม

อาจเป็น RDMBS + MongoDB แต่นั่นจะเพิ่มความซับซ้อนและจะทำให้การจัดการและสนับสนุนแอปพลิเคชันยากขึ้น


1
ขณะนี้มีการเผยแพร่ MongoDB ชื่อ TokuMX ที่ใช้เทคโนโลยีเศษส่วนเพื่อส่งมอบการปรับปรุงประสิทธิภาพ 50x และให้การสนับสนุนธุรกรรมกรดในเวลาเดียวกัน: tokutek.com/tokumx-for-mongodb
OCDev

9
การทำธุรกรรมไม่เคยเป็น "ต้อง" ได้อย่างไร ทันทีที่คุณต้องการกรณีง่าย ๆ 1 กรณีที่คุณจำเป็นต้องอัปเดต 2 ตาราง mongo ก็ไม่เหมาะสมอีกหรือ ที่ไม่ทิ้งกรณีการใช้งานจำนวนมากเลย
Mr_E

1
@Mr_E เห็นด้วยนั่นคือเหตุผลที่ MongoDB เป็นคนโง่ :)
Alexander Mills

6

นี่อาจเป็นบล็อกที่ดีที่สุดที่ฉันพบเกี่ยวกับการใช้งานธุรกรรมเช่นคุณลักษณะสำหรับ mongodb!

การซิงค์ตั้งค่าสถานะ: ดีที่สุดเพียงคัดลอกข้อมูลจากเอกสารหลัก

คิวงาน: วัตถุประสงค์ทั่วไปมากสามารถแก้ปัญหาได้ 95% ระบบส่วนใหญ่จำเป็นต้องมีคิวงานอย่างน้อยหนึ่งคิวอยู่ดี!

Commit สองเฟส: เทคนิคนี้ทำให้มั่นใจได้ว่าแต่ละเอนทิตีมีข้อมูลทั้งหมดที่จำเป็นในการเข้าสู่สถานะที่สอดคล้องกัน

การกระทบยอดเข้าสู่ระบบ: เทคนิคที่แข็งแกร่งที่สุดเหมาะสำหรับระบบการเงิน

การกำหนดเวอร์ชัน: ให้การแยกและสนับสนุนโครงสร้างที่ซับซ้อน

อ่านเพื่อรับข้อมูลเพิ่มเติม: https://dzone.com/articles/how-implement-robust-and


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

ขอบคุณ @mech สำหรับคำแนะนำ
Vaibhav

4

นี่มันสายไปแล้ว แต่คิดว่ามันจะช่วยได้ในอนาคต ฉันใช้Redisเพื่อสร้างคิวเพื่อแก้ปัญหานี้

  • ความต้องการ:
    ภาพด้านล่างแสดง 2 การกระทำที่ต้องดำเนินการพร้อมกัน แต่ขั้นตอนที่ 2 และขั้นตอนที่ 3 ของการดำเนินการ 1 ต้องเสร็จสิ้นก่อนที่จะเริ่มเฟส 2 ของการกระทำ 2 หรือตรงข้าม (เฟสสามารถร้องขอ REST api คำขอฐานข้อมูล ) ป้อนคำอธิบายรูปภาพที่นี่

  • วิธีการที่คิวช่วยคุณในการ
    ตรวจสอบให้แน่ใจว่าทุก ๆ บล็อคโค้ดระหว่างlock()และrelease()ในหลาย ๆ ฟังก์ชั่นจะไม่ทำงานในเวลาเดียวกันทำให้แยกได้

    function action1() {
      phase1();
      queue.lock("action_domain");
      phase2();
      phase3();
      queue.release("action_domain");
    }
    
    function action2() {
      phase1();
      queue.lock("action_domain");
      phase2();
      queue.release("action_domain");
    }
  • จะสร้างคิวได้อย่างไร
    ฉันจะเน้นที่การหลีกเลี่ยงเรซ conditonส่วนเมื่อสร้างคิวบนไซต์ backend หากคุณไม่ทราบความคิดพื้นฐานของคิวมาที่นี่
    รหัสด้านล่างแสดงแนวคิดเท่านั้นคุณต้องใช้ในวิธีที่ถูกต้อง

    function lock() {
      if(isRunning()) {
        addIsolateCodeToQueue(); //use callback, delegate, function pointer... depend on your language
      } else {
        setStateToRunning();
        pickOneAndExecute();
      }
    }
    
    function release() {
      setStateToRelease();
      pickOneAndExecute();
    }

แต่คุณต้องisRunning() setStateToRelease() setStateToRunning()แยกตัวมันออกเองมิฉะนั้นคุณจะต้องเจอกับสภาพการแข่งขันอีกครั้ง เมื่อต้องการทำสิ่งนี้ฉันเลือก Redis สำหรับกรดเป้าหมายและปรับขนาดได้
Redis documentพูดคุยเกี่ยวกับการทำธุรกรรม:

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

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


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

3

ธุรกรรมมีให้บริการแล้วใน MongoDB 4.0 ตัวอย่างที่นี่

// Runs the txnFunc and retries if TransientTransactionError encountered

function runTransactionWithRetry(txnFunc, session) {
    while (true) {
        try {
            txnFunc(session);  // performs transaction
            break;
        } catch (error) {
            // If transient error, retry the whole transaction
            if ( error.hasOwnProperty("errorLabels") && error.errorLabels.includes("TransientTransactionError")  ) {
                print("TransientTransactionError, retrying transaction ...");
                continue;
            } else {
                throw error;
            }
        }
    }
}

// Retries commit if UnknownTransactionCommitResult encountered

function commitWithRetry(session) {
    while (true) {
        try {
            session.commitTransaction(); // Uses write concern set at transaction start.
            print("Transaction committed.");
            break;
        } catch (error) {
            // Can retry commit
            if (error.hasOwnProperty("errorLabels") && error.errorLabels.includes("UnknownTransactionCommitResult") ) {
                print("UnknownTransactionCommitResult, retrying commit operation ...");
                continue;
            } else {
                print("Error during commit ...");
                throw error;
            }
       }
    }
}

// Updates two collections in a transactions

function updateEmployeeInfo(session) {
    employeesCollection = session.getDatabase("hr").employees;
    eventsCollection = session.getDatabase("reporting").events;

    session.startTransaction( { readConcern: { level: "snapshot" }, writeConcern: { w: "majority" } } );

    try{
        employeesCollection.updateOne( { employee: 3 }, { $set: { status: "Inactive" } } );
        eventsCollection.insertOne( { employee: 3, status: { new: "Inactive", old: "Active" } } );
    } catch (error) {
        print("Caught exception during transaction, aborting.");
        session.abortTransaction();
        throw error;
    }

    commitWithRetry(session);
}

// Start a session.
session = db.getMongo().startSession( { mode: "primary" } );

try{
   runTransactionWithRetry(updateEmployeeInfo, session);
} catch (error) {
   // Do something with error
} finally {
   session.endSession();
}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.