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


13

เรามีสถานการณ์ที่ฉันต้องรับมือกับเหตุการณ์ที่ไหลเข้ามาในเซิร์ฟเวอร์ของเราโดยเฉลี่ยประมาณ 1,000 เหตุการณ์ต่อวินาทีโดยเฉลี่ย

ปัญหา

ระบบของเราโฮสต์บนHerokuและใช้Heroku Postgres DBที่ค่อนข้างแพงซึ่งอนุญาตการเชื่อมต่อ DB ได้สูงสุด 500 เราใช้การเชื่อมต่อร่วมกันเพื่อเชื่อมต่อจากเซิร์ฟเวอร์ไปยังฐานข้อมูล

เหตุการณ์เข้ามาเร็วกว่าการเชื่อมต่อฐานข้อมูลที่สามารถจัดการได้

ปัญหาที่เรามีคือเหตุการณ์เกิดขึ้นเร็วกว่าพูลการเชื่อมต่อที่สามารถจัดการได้ เมื่อถึงเวลาที่การเชื่อมต่อหนึ่งเสร็จสิ้นการส่งสัญญาณเครือข่ายจากเซิร์ฟเวอร์ไปยังฐานข้อมูลดังนั้นจึงสามารถปล่อยกลับไปที่กลุ่มได้มากกว่าnมีเหตุการณ์เพิ่มเติมเข้ามา

ในที่สุดเหตุการณ์ต่างๆก็หมดลงรอรับการบันทึกและเนื่องจากไม่มีการเชื่อมต่อที่พร้อมใช้งานในกลุ่มจึงหมดเวลาและระบบทั้งหมดไม่สามารถใช้งานได้

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

ข้อ จำกัด

ลูกค้ารายอื่นอาจต้องการอ่านเหตุการณ์พร้อมกัน

ไคลเอนต์อื่น ๆ ร้องขออย่างต่อเนื่องเพื่ออ่านเหตุการณ์ทั้งหมดที่มีคีย์เฉพาะแม้ว่าพวกเขาจะยังไม่ได้บันทึกในฐานข้อมูล

ไคลเอนต์สามารถสอบถามGET api/v1/events?clientId=1และรับเหตุการณ์ทั้งหมดที่ส่งโดยไคลเอนต์ 1 แม้ว่าเหตุการณ์เหล่านั้นจะยังไม่ได้ทำการบันทึกในฐานข้อมูล

มีตัวอย่าง "ห้องเรียน" เกี่ยวกับวิธีจัดการกับเรื่องนี้หรือไม่?

การแก้ปัญหาที่เป็นไปได้

จัดคิวเหตุการณ์บนเซิร์ฟเวอร์ของเรา

เราสามารถจัดคิวเหตุการณ์บนเซิร์ฟเวอร์ (ด้วยคิวที่มีการเกิดพร้อมกันสูงสุด 400 เพื่อให้กลุ่มการเชื่อมต่อไม่หมด)

นี่เป็นความคิดที่ไม่ดีเพราะ:

  • มันจะกินหน่วยความจำเซิร์ฟเวอร์ที่มีอยู่ เหตุการณ์ที่จัดคิวเข้าด้วยกันจะใช้ RAM จำนวนมาก
  • เซิร์ฟเวอร์ของเราเริ่มต้นใหม่ครั้งเดียวทุก 24 ชั่วโมง นี่เป็นข้อ จำกัด อย่างหนักจาก Heroku เซิร์ฟเวอร์สามารถรีสตาร์ทในขณะที่เหตุการณ์ถูกจัดคิวทำให้เราสูญเสียเหตุการณ์ที่จัดคิว
  • มันแนะนำสถานะบนเซิร์ฟเวอร์จึงทำร้ายความยืดหยุ่น หากเรามีการตั้งค่าเซิร์ฟเวอร์หลายเครื่องและลูกค้าต้องการอ่านเหตุการณ์ที่จัดคิว + ที่บันทึกไว้ทั้งหมดเราจะไม่ทราบว่าเซิร์ฟเวอร์ใดที่มีเหตุการณ์ที่จัดคิวอยู่

ใช้คิวข้อความแยกต่างหาก

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

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

ใช้หลายฐานข้อมูลแต่ละส่วนบันทึกข้อความด้วยเซิร์ฟเวอร์ประสานงานฐานข้อมูลส่วนกลางเพื่อจัดการพวกเขา

วิธีแก้ปัญหาอื่นที่เราใช้คือการใช้ฐานข้อมูลหลายตัวโดยมี "DB ผู้ประสานงาน / โหลดบาลานเซอร์" ส่วนกลาง เมื่อได้รับเหตุการณ์ผู้ประสานงานนี้จะเลือกหนึ่งในฐานข้อมูลเพื่อเขียนข้อความ สิ่งนี้จะช่วยให้เราสามารถใช้ฐานข้อมูล Heroku หลายฐานจึงเพิ่มขีด จำกัด การเชื่อมต่อกับฐานข้อมูล 500 x จำนวน

เมื่ออ่านแบบสอบถามผู้ประสานงานนี้สามารถออกSELECTแบบสอบถามไปยังแต่ละฐานข้อมูลรวมผลลัพธ์ทั้งหมดและส่งกลับไปยังไคลเอนต์ที่ร้องขอการอ่าน

นี่เป็นความคิดที่ไม่ดีเพราะ:

  • ความคิดนี้ดูเหมือนจะ ... อะแฮ่ม. จะเป็นฝันร้ายในการจัดการเช่นกัน (สำรองข้อมูล ฯลฯ ) มันมีความซับซ้อนในการสร้างและบำรุงรักษาและหากจำเป็นอย่างยิ่งมันฟังดูเหมือนเป็นการละเมิดKISS
  • มันเสียสละความสอดคล้อง การทำธุรกรรมข้ามฐานข้อมูลหลายรายการนั้นไม่ต้องดำเนินการหากเราไปพร้อมกับแนวคิดนี้

3
คอขวดของคุณอยู่ที่ไหน คุณกำลังพูดถึงพูลการเชื่อมต่อของคุณ แต่มีผลกับการขนานเท่านั้นไม่ใช่ความเร็วต่อการแทรก หากคุณมีการเชื่อมต่อ 500 รายการและเช่น 2000QPS การดำเนินการนี้ควรใช้งานได้ดีหากการสืบค้นแต่ละรายการเสร็จสิ้นภายใน 250ms ซึ่งเป็นเวลาที่นานมาก ทำไมถึงสูงกว่า 15ms นอกจากนี้โปรดทราบว่าการใช้ PaaS จะทำให้คุณมีโอกาสเพิ่มประสิทธิภาพที่สำคัญเช่นการปรับขนาดฮาร์ดแวร์ฐานข้อมูลหรือการใช้แบบจำลองการอ่านเพื่อลดภาระในฐานข้อมูลหลัก Heroku ไม่คุ้มค่าหากการใช้งานเป็นปัญหาที่ใหญ่ที่สุดของคุณ
อมร

@amon คอขวดเป็นสระเชื่อมต่อแน่นอน ฉันใช้ANALYZEคำสั่งด้วยตนเองและพวกเขาก็ไม่เป็นปัญหา ฉันได้สร้างต้นแบบเพื่อทดสอบสมมติฐานกลุ่มการเชื่อมต่อและตรวจสอบว่านี่เป็นปัญหา ฐานข้อมูลและเซิร์ฟเวอร์อยู่ในเครื่องที่แตกต่างกันดังนั้นเวลาแฝง นอกจากนี้เราไม่ต้องการให้ Heroku ยกเว้นกรณีที่จำเป็นอย่างแท้จริงไม่ต้องกังวลเกี่ยวกับการปรับใช้เป็นประโยชน์อย่างมากสำหรับเรา
Nik Kyriakides

1
ที่ถูกกล่าวว่าผมเข้าใจว่ามีไมโคร optimisations ที่ฉันจะทำอย่างไรที่จะช่วยให้ผมแก้ปัญหาในปัจจุบันปัญหา ฉันสงสัยว่ามีวิธีแก้ไขปัญหาทางสถาปัตยกรรมที่ปรับขนาดได้สำหรับปัญหาของฉันหรือไม่
Nik Kyriakides

3
คุณยืนยันได้อย่างไรว่ากลุ่มการเชื่อมต่อเป็นปัญหา @amon ถูกต้องในการคำนวณของเขา ลองใช้select nullกับคนรู้จัก 500 คน ฉันเดิมพันคุณจะพบว่ากลุ่มการเชื่อมต่อนั้นไม่ใช่ปัญหา
usr

1
ถ้าเลือก null เป็นปัญหาคุณอาจพูดถูก แม้ว่ามันจะเป็นที่น่าสนใจที่ทุกเวลาที่ใช้ ไม่มีเครือข่ายที่ช้า
usr

คำตอบ:


9

สตรีมอินพุต

ไม่ชัดเจนว่า 1,000 เหตุการณ์ / วินาทีของคุณแสดงถึงจุดสูงสุดหรือหากเป็นการโหลดอย่างต่อเนื่อง:

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

ทางออกที่เสนอ

ในทั้งสองกรณีฉันจะไปหาอีเวนต์สตรีมจากคาฟคา :

  • กิจกรรมทั้งหมดได้รับการเผยแพร่อย่างเป็นระบบในหัวข้อคาฟคา
  • ผู้บริโภคจะสมัครสมาชิกกับกิจกรรมและเก็บไว้ในฐานข้อมูล
  • ตัวประมวลผลแบบสอบถามจะจัดการการร้องขอจากไคลเอนต์และสอบถาม DB

สามารถปรับขนาดได้อย่างมากในทุกระดับ:

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

การเสนอกิจกรรมที่ยังไม่ได้เขียนในฐานข้อมูลให้กับลูกค้า

คุณต้องการให้ลูกค้าของคุณสามารถเข้าถึงยังข้อมูลที่อยู่ในท่อและยังไม่ได้เขียนไปยังฐานข้อมูล นี่เป็นความละเอียดอ่อนอีกเล็กน้อย

ตัวเลือกที่ 1: การใช้แคชเพื่อเติมเต็มแบบสอบถามเคียวรี

ฉันยังไม่ได้วิเคราะห์ในเชิงลึก แต่ความคิดแรกที่มาถึงใจของฉันจะทำให้การประมวลผลแบบสอบถาม (s) ผู้บริโภค (s) ของ Kafka หัวข้อ แต่ในที่แตกต่างกันของกลุ่มผู้บริโภค Kafka ตัวประมวลผลการร้องขอจะได้รับข้อความทั้งหมดที่ตัวเขียน DB จะได้รับ แต่เป็นอิสระ มันสามารถเก็บไว้ในแคชท้องถิ่น แบบสอบถามจะทำงานบน DB + cache (+ กำจัดการทำซ้ำ)

การออกแบบจะมีลักษณะดังนี้:

ป้อนคำอธิบายรูปภาพที่นี่

ความสามารถในการปรับขนาดของเลเยอร์แบบสอบถามนี้สามารถทำได้โดยการเพิ่มตัวประมวลผลคิวรีเพิ่มเติม (แต่ละกลุ่มผู้บริโภคของตัวเอง)

ตัวเลือก 2: ออกแบบ API คู่

วิธีที่ดีกว่า IMHO คือเสนอ API แบบคู่ (ใช้กลไกของกลุ่มผู้บริโภคแยกต่างหาก):

  • API การสืบค้นสำหรับการเข้าถึงเหตุการณ์ในฐานข้อมูลและ / หรือการวิเคราะห์
  • API การสตรีมที่ส่งต่อข้อความโดยตรงจากหัวข้อ

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

สายพันธุ์

ฉันเสนอคาฟคาเพราะมันถูกออกแบบมาสำหรับปริมาณที่สูงมากพร้อมกับข้อความถาวรเพื่อให้คุณสามารถรีสตาร์ทเซิร์ฟเวอร์หากจำเป็น

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


ดาวฤกษ์; คุณหมายถึงa distributed database (for example using a specialization of the server by group of keys)อะไร ทำไมคาฟคาแทน RabbitMQ มีเหตุผลพิเศษในการเลือกอย่างใดอย่างหนึ่งหรือไม่?
Nik Kyriakides

@NicholasKyriakides ขอบคุณ! 1) ฉันแค่คิดถึงเซิร์ฟเวอร์ฐานข้อมูลอิสระหลายตัว แต่มีรูปแบบการแบ่งพาร์ติชันที่ชัดเจน (คีย์, ภูมิศาสตร์, ฯลฯ .. ) ที่สามารถใช้ในการส่งคำสั่งได้อย่างมีประสิทธิภาพ 2) อย่างสังหรณ์ใจอาจเป็นเพราะ Kafka ได้รับการออกแบบสำหรับปริมาณงานที่สูงมากพร้อมกับข้อความที่ไม่หยุดนิ่งจำเป็นต้องรีสตาร์ทเซิร์ฟเวอร์ของคุณ?) ฉันไม่แน่ใจว่า RabbitMQ มีความยืดหยุ่นสำหรับสถานการณ์แบบกระจายและคิวแบบถาวรช่วยลดประสิทธิภาพ
Christophe

สำหรับ 1) นี่ก็คล้ายกับUse multiple databasesความคิดของฉันแต่คุณกำลังบอกว่าฉันไม่ควรจะสุ่ม (หรือปัดเศษ) เพื่อกระจายข้อความไปยังฐานข้อมูลใด ๆ ขวา?
Nik Kyriakides

ใช่. ความคิดแรกของฉันจะไม่ไปกระจายแบบสุ่มเพราะอาจเพิ่มภาระการประมวลผลสำหรับการสืบค้น (เช่นแบบสอบถามของฐานข้อมูลทั้งสองส่วนใหญ่ใช้เวลาส่วนใหญ่) คุณสามารถพิจารณาเครื่องยนต์ DB แบบกระจาย (เช่นIgnite?) แต่หากต้องการตัวเลือกที่มีข้อมูลใด ๆ จะต้องมีความเข้าใจที่ดีเกี่ยวกับรูปแบบการใช้งานฐานข้อมูล (สิ่งอื่นอยู่ในฐานข้อมูลบ่อยแค่ไหนที่ถูกสอบถามคำถามประเภทใดจะมีข้อ จำกัด ในการทำธุรกรรมมากกว่าแต่ละกิจกรรม ฯลฯ )
Christophe

3
แค่อยากบอกว่าถึงแม้คาฟก้าจะให้ปริมาณงานสูงมาก แต่ก็อาจเกินความต้องการของคนส่วนใหญ่ ฉันพบว่าการจัดการกับคาฟคาและ API ของมันเป็นความผิดพลาดครั้งใหญ่สำหรับเรา RabbitMQ ไม่ใช่เรื่องเหลวไหลและมีอินเทอร์เฟซที่คุณคาดหวังจาก MQ
imel96

11

ฉันเดาว่าคุณต้องสำรวจวิธีการที่คุณปฏิเสธอย่างระมัดระวังมากขึ้น

  • จัดคิวเหตุการณ์บนเซิร์ฟเวอร์ของเรา

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

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

เซิร์ฟเวอร์สามารถรีสตาร์ทในขณะที่เหตุการณ์ถูกจัดคิวทำให้เราสูญเสียเหตุการณ์ที่จัดคิว

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

  • ใช้หลายฐานข้อมูลแต่ละส่วนบันทึกข้อความด้วยเซิร์ฟเวอร์ประสานงานฐานข้อมูลส่วนกลางเพื่อจัดการพวกเขา

อาจ - ฉันมีแนวโน้มที่จะดูขอบเขตธุรกิจของคุณเพื่อดูว่ามีสถานที่ตามธรรมชาติที่จะจัดเก็บข้อมูลหรือไม่

มีบางกรณีที่การสูญเสียข้อมูลเป็นข้อแลกเปลี่ยนที่ยอมรับได้?

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

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

ดูการส่งข้อความที่เชื่อถือได้โดยไม่ต้องทำธุรกรรมโดย Udi Dahan (อ้างอิงโดยAndy แล้ว ) และPolyglot Dataโดย Greg Young


In a distributed system, I think you can be pretty confident that messages are going to get lost. จริงๆ? มีบางกรณีที่การสูญเสียข้อมูลเป็นข้อแลกเปลี่ยนที่ยอมรับได้? ฉันอยู่ภายใต้การแสดงผลที่สูญเสียข้อมูล = ความล้มเหลว
Nik Kyriakides

1
@NicholasKyriakides มันมักจะไม่เป็นที่ยอมรับดังนั้น OP จึงแนะนำความเป็นไปได้ที่จะเขียนไปยังร้านค้าที่ทนทานก่อนที่จะส่งงาน ตรวจสอบบทความนี้และวิดีโอนี้โดย Udi Dahan ที่ซึ่งเขาได้แก้ไขปัญหาในรายละเอียดเพิ่มเติม
แอนดี้

6

ถ้าฉันเข้าใจถูกต้องกระแสปัจจุบันคือ:

  1. รับและจัดกิจกรรม (ฉันถือว่าผ่าน HTTP หรือไม่)
  2. ร้องขอการเชื่อมต่อจากพูล
  3. แทรกเหตุการณ์ลงในฐานข้อมูล
  4. ปล่อยการเชื่อมต่อกับพูล

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

ใช้การเรียงลำดับของคิวที่เกิดขึ้นพร้อมกันจากนั้นให้เธรดเหล่านี้ดึงข้อความจากคิวที่เกิดขึ้นพร้อมกันและแทรก ในทางทฤษฎีแล้วพวกเขาไม่จำเป็นต้องกลับมาเชื่อมต่อกับกลุ่มหรือขอใหม่ แต่คุณอาจต้องสร้างในการจัดการในกรณีที่การเชื่อมต่อไม่ดี มันอาจจะง่ายที่สุดในการฆ่าเธรด / กระบวนการและเริ่มใหม่

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


5

สมมติฐาน

ฉันจะสมมติว่าภาระที่คุณอธิบายนั้นคงที่เนื่องจากเป็นสถานการณ์ที่ยากขึ้นในการแก้ไข

ฉันจะสมมติว่าคุณมีวิธีการเรียกใช้งานปริมาณงานระยะยาวนอกกระบวนการเว็บแอปพลิเคชันของคุณ

วิธีการแก้

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

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

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

คำตอบ SO นี้อธิบายวิธีที่เร็วที่สุดในการล้างข้อมูลใน Postgres - แม้ว่าจะต้องให้แบทช์ของคุณเก็บคิวไว้บนดิสก์

การ จำกัด

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

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


3

เหตุการณ์เข้ามาเร็วกว่าการเชื่อมต่อฐานข้อมูลที่สามารถจัดการได้

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

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

ลูกค้ารายอื่นอาจต้องการอ่านเหตุการณ์พร้อมกัน

ข้อ จำกัด นี้เป็นไปได้หากเหตุการณ์ที่เก็บไว้ในฐานข้อมูลโดยไม่มีการประมวลผลใด ๆ (เหตุการณ์ดิบ) หากเหตุการณ์ได้รับการประมวลผลก่อนที่จะเก็บไว้ในฐานข้อมูลวิธีเดียวที่จะได้รับเหตุการณ์นั้นมาจากฐานข้อมูล

หากลูกค้าเพียงแค่ต้องการค้นหาเหตุการณ์ดิบฉันจะแนะนำให้ใช้เครื่องมือค้นหาเช่น Elastic Search คุณจะได้รับแบบสอบถาม / API การค้นหาได้ฟรี

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

Scaling Elastic Search นั้นง่าย แต่แม้จะมีการกำหนดค่าพื้นฐาน แต่ก็มีประสิทธิภาพสูง

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


2

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

ลูกค้ารายอื่นอาจต้องการอ่านเหตุการณ์พร้อมกัน

สำหรับคำขอเหล่านี้พวกเขาจะต้องอ่านจาก master db เนื่องจากจะมีการจำลองแบบล่าช้าไปยังทาสที่อ่าน

ฉันใช้ (Percona) MySQL กับเครื่องมือ TokuDB สำหรับการเขียนปริมาณมาก นอกจากนี้ยังมีเครื่องยนต์ MyRocks ที่ใช้ LSMtrees ซึ่งดีสำหรับการเขียนข้อมูล สำหรับเอ็นจิ้นเหล่านี้และมีแนวโน้มว่า PostgreSQL จะมีการตั้งค่าสำหรับการแยกธุรกรรมเช่นเดียวกับการกระทำการซิงค์ซึ่งสามารถเพิ่มความสามารถในการเขียนได้อย่างมาก ในอดีตเรายอมรับข้อมูลที่สูญหายมากถึง 1 ซึ่งรายงานไปยังลูกค้า db ตามที่ได้ตกลงไว้ ในกรณีอื่น ๆ มี SSD แบตเตอรี่สำรองเพื่อหลีกเลี่ยงการสูญเสีย

Amazon RDS Aurora ในรสชาติของ MySQL นั้นมีอัตราการเขียนข้อมูลที่สูงขึ้น 6 เท่าด้วยการทำซ้ำแบบไม่มีค่าใช้จ่าย (คล้ายกับทาสที่แชร์ระบบไฟล์กับมาสเตอร์) รสชาติของ Aurora PostgreSQL ยังมีกลไกการจำลองแบบขั้นสูงที่แตกต่างกัน


TBH ฐานข้อมูลที่ได้รับการยอมรับเป็นอย่างดีเกี่ยวกับฮาร์ดแวร์ที่เพียงพอควรสามารถรับมือกับโหลดนี้ได้ ปัญหาของ OP ดูเหมือนจะไม่ใช่ประสิทธิภาพของฐานข้อมูล แต่แฝงการเชื่อมต่อ ฉันเดาว่า Heroku ในฐานะผู้ให้บริการ PaaS กำลังขายอินสแตนซ์ Postgres ในภูมิภาค AWS อื่นให้พวกเขา
amon

1

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

โชคดี

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