วิธีแก้ไขการขาดธุรกรรมใน 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.