CobbleDB: การสร้างพื้นที่จัดเก็บการค้นหา AI ใหม่เพื่อความหน่วงและต้นทุนที่ลดลง

สถาปัตยกรรมแบบแยกส่วนที่แยกสถานะคงทน การอัปเดตแบบแบตช์ และการดึงข้อมูลขณะคิวรีออกจากกัน

ผู้เขียนPerplexity Engineering

การค้นหาแบบ AI-native กำหนดข้อกำหนดที่แตกต่างกันเกี่ยวกับวิธีที่เนื้อหาเว็บจำเป็นต้องได้รับการประมวลผล จัดเก็บ และให้บริการ เพื่อตอบสนองความต้องการเหล่านั้นในสเกลใหญ่ เราจึงได้สร้างสแต็กทั้งหมดขึ้นมาใหม่ตั้งแต่ต้นเพื่อปรับปรุงประสิทธิภาพและลดต้นทุน

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

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

ด้วยความช่วยเหลือจากกลุ่มโค้ดดิ้งเอเจนต์ภายในตัวใหม่ เราได้สร้างเลเยอร์นี้ขึ้นมาใหม่รอบๆ CobbleDB ซึ่งเป็นสโตร์แบบฮอตแบบคีย์-แวลู โดยได้รับการสนับสนุนจาก Pillar สำหรับสถานะเอกสารแบบคงทนและการเผยแพร่ และ Lorry สำหรับการอัปเดตแบบแบตช์ การออกแบบใหม่นี้ช่วยแยกการประมวลผลเอกสารออกจากกระบวนการนำเข้าสโตร์แบบฮอต และช่วยให้เราสามารถปรับแต่งการให้บริการให้เข้ากับเวิร์กโหลดของเราได้ การวัดผลระบบจริงก่อนและหลังแสดงให้เห็นว่าความหน่วงในการอ่านแบบแบตช์ลดลงประมาณ 5 เท่า และแบบจำลองต้นทุนภายในของเราประเมินว่าประหยัดได้ 0 อย่างน้อย 20% เมื่อเทียบกับ DynamoDB

โครงสร้างพื้นฐานการจัดเก็บข้อมูลสำหรับการค้นหาแบบ AI-native

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

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

สองเวิร์กโฟลว์ที่แตกต่างกัน

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

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

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

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

จุดที่ฐานข้อมูลที่มีการจัดการยังทำได้ไม่ดีพอ

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

ประการที่สอง การคิดราคาตามการใช้งานจะคิดเงินทุกไบต์ที่มีการอ่านหรือเขียน คำขอ Search API หนึ่งรายการประกอบด้วยคีย์ 100-120 คีย์ที่สอดคล้องกับหน้าเว็บที่เราต้องดึง กระบวนการดึงข้อมูลจะแบ่งคำขอออกเป็นแบตช์ขนาดเล็กๆ 10-20 คีย์หน้าเว็บ โดยมีขนาดรายการเฉลี่ยประมาณ 50 KB การอ่านเรกคอร์ดเหล่านั้นซ้ำๆ กินความจุไปมาก ในขณะที่การรวบรวมข้อมูลอย่างต่อเนื่องและการเปลี่ยนแปลงโมเดลการแบ่งชังค์หรือการฝังก็ทำให้เกิดการเขียนมากขึ้นเช่นกัน นอกจากนี้ เศรษฐศาสตร์ยังขึ้นอยู่กับขนาดและความถี่ของเรกคอร์ด ไม่ใช่แค่จำนวนการเรียกใช้ API เท่านั้น

ประการที่สาม บริการที่มีการจัดการขาดความยืดหยุ่นที่เราต้องการสำหรับการปรับแต่งประสิทธิภาพ การดำเนินการที่เราต้องการนั้นแคบมาก นั่นคือ เมื่อระบุชุดตัวระบุหน้าเว็บมา ให้คืนค่าเนื้อหาที่เตรียมไว้ให้อย่างรวดเร็ว เราต้องการควบคุมว่าเครื่องใดถือพาร์ทชันใด มีหน่วยความจำเท่าใดที่อุทิศให้กับแคช และเรพลิกาใดจัดการคำขอ การควบคุมเหล่านี้มีความสำคัญเนื่องจากการอ่านที่ไม่ได้แคช การกระโดดข้ามโซน หรือเรพลิกาที่ช้าสามารถทำให้แบตช์ทั้งหมดล่าช้าได้ DynamoDBจัดการพื้นที่จัดเก็บและพฤติกรรมเรพลิกาของตนเองเบื้องหลัง API เราสามารถกำหนดค่าบริการได้ แต่เราไม่สามารถปรับแต่งตำแหน่งภายใน แคชของเอ็นจิ้นการจัดเก็บ หรือนโยบายการเลือกเรพลิกาให้เข้ากับเวิร์กโหลดของเราได้

ประการที่สาม การประมวลผลถูกผูกติดโดยตรงกับการเขียนสโตร์แบบฮอต ไพพไลน์การประมวลผลก่อนหน้าของเราเขียนหน้าเว็บที่เตรียมไว้แต่ละหน้าลงใน DynamoDB โดยตรง ดังนั้น งานประมวลผลซ้ำขนาดใหญ่จึงกลายเป็นการอัปเดตสโตร์แบบฮอตแต่ละรายการจำนวนมาก และการประมวลผลจึงต้องรองรับขีดจำกัดปริมาณงานและพฤติกรรมการลองใหม่ของสโตร์แบบฮอตด้วย เราไม่สามารถเรียกใช้ MapReduce ทั่วทั้งฐานข้อมูลภายใน 1-2 วันเพื่อลองใช้วิธีการแบ่งส่วนแบบใหม่ เปลี่ยนโมเดลการฝัง หรือเพิ่มฟิลด์ใหม่ได้ ไม่มีสตรีมอิสระของการอัปเดตหน้าเว็บที่บันทึกไว้ซึ่งบริการแยกต่างหากสามารถนำมาจัดเป็นแบตช์และส่งมอบให้กับสโตร์แบบฮอตตามจังหวะของตนเองได้

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

แยกสถานะเอกสาร การส่งมอบการอัปเดต และการให้บริการออกจากกัน

สถาปัตยกรรมใหม่กำหนดความรับผิดชอบแต่ละอย่างให้กับคอมเพอนนต์ที่แตกต่างกัน Pillar รักษาเสถียรภาพของสถานะเอกสารแบบคงทนและตัดสินใจว่าจะเผยแพร่สิ่งใด Lorry แปลงการส่งออกเหล่านั้นให้เป็นแบตช์ที่แบ่งพาร์ทชัน CobbleDB ดึงข้อมูลแบตช์เข้ามาและให้บริการเรกคอร์ดที่เตรียมไว้ตามคีย์หน้าเว็บ

ก่อนและหลังการย้ายการจัดเก็บข้อมูล Pillar และ Lorry แยกสถานะเอกสารแบบทนทานและการส่งมอบอัปเดตออกจากการอ่านที่ให้บริการโดย CobbleDB ในเวลาคิวรี

CobbleDB: เส้นทางการอ่านที่ปรับแต่งได้

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

การเปลี่ยนแปลงที่สำคัญคือการควบคุมการกำหนดค่าพื้นที่จัดเก็บข้อมูลของเรา

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

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

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

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

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

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

เราเตอร์แบบ stateless จัดกลุ่มคีย์ของเพจตามพาร์ทิชันและอ่าน replica แบบขนาน การอ่านที่ช้าอาจถูกส่งไปยัง replica อื่น และผลลัพธ์แรกของแต่ละพาร์ทิชันจะถูกรวมเข้ากับผลลัพธ์

Pillar และ Lorry: เส้นทางการเขียนแบบคงทน

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

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

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

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

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

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

Pillar เผยแพร่การส่งออกแบบทนทาน และ Lorry เขียนแบตช์แบบแบ่งพาร์ทิชันไปยัง S3 และลงทะเบียนแบตช์เหล่านั้นกับ control plane แต่ละ replica ของ CobbleDB ค้นพบและ ingest แบตช์ของตนเองอย่างอิสระตามลำดับเวลา

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

การตรวจสอบความถูกต้อง

เร็วขึ้น 5 เท่า

หลังจากการย้ายข้อมูลสโตร์แบบฮอต ค่ามัธยฐานของความหน่วงในการอ่านแบบแบตช์ลดลงจาก 31.4 มิลลิวินาที เหลือ 5.60 มิลลิวินาที ค่า p90 ลดลงจาก 56.7 มิลลิวินาที เหลือ 9.77 มิลลิวินาที และค่า p99 ลดลงจาก 123 มิลลิวินาที เหลือ 24.2 มิลลิวินาที สัดส่วนดังกล่าวมีตั้งแต่ 5.08× ถึง 5.80× พร้อมทั้งมีการปรับปรุงที่ดีขึ้นทั้งในส่วนของคำขอทั่วไปและหางการกระจาย (tail)

ความหน่วงในการอ่านแบตช์ระดับโปรดักชันก่อนและหลังการย้าย: P50 ลดลงจาก 31.4 เป็น 5.60 ms, P90 จาก 56.7 เป็น 9.77 ms, และ P99 จาก 123 เป็น 24.2 ms นี่คือความหน่วงการอ่านจาก hot store จากทราฟฟิกจริงในช่วงเวลาต่าง ๆ ไม่ใช่คำขอที่จับคู่กันหรือความหน่วงการตอบคำถามแบบ end-to-end

ตัวเลขเหล่านี้ได้มาจากการอ่านแบบแบตช์ โดยแต่ละคิวรีจะร้องขอคีย์ประมาณ 10-15 คีย์ ขนาดรายการเพย์โหลดเฉลี่ยคือ 50KB ระบบทั้งสองถูกคิวรีที่ประมาณ 200k rps แม้ว่าเราจะทดสอบโหลดสูงถึง 500k rps โดยไม่มีการเสื่อมประสิทธิภาพก็ตาม

เพื่อแยกแยะการมีส่วนร่วมของ CobbleDB องค์ประกอบอื่นๆ ทั้งหมดของการตั้งค่าจึงถูกรักษาไว้ให้คงที่ อย่างไรก็ตาม นี่เป็นเพียงการเปรียบเทียบเชิงสังเกตการณ์แบบก่อนและหลังเท่านั้น ระบบต่างๆให้บริการทราฟฟิกการใช้งานจริงในเวลาที่ต่างกัน แทนที่จะเป็นคำขอเดียวกันภายใต้เงื่อนไขที่มีการควบคุม ดังนั้นเราจึงวัดประสิทธิภาพของทั้งสองระบบในชุดทดสอบสังเคราะห์ด้วย เพื่อให้ใกล้เคียงกับคำขอที่พบในการใช้งานจริง เราจึงทำการคิวรีทั้งสองระบบด้วยแบตช์ขนาด 10-15 คีย์และค่าที่มีขนาดตั้งแต่ 100B ถึง 100 KiB

ความหน่วงในการอ่านแบตช์สังเคราะห์สำหรับแบตช์ขนาด 10 และ 15 คีย์ ครอบคลุมขนาดค่าตั้งแต่ 100 B ถึง 100 KiB แต่ละจุดคือค่าเฉลี่ยของความหน่วงระดับการรันแบบ P50, P90 หรือ P99 จากการรันสองครั้งต่อแบ็กเอนด์และการกำหนดค่า

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

ถูกกว่า 20%

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

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

กลุ่มเอเจนต์ AI ช่วยให้เราสร้างมันขึ้นมาได้อย่างไร

การสร้าง CobbleDB และโครงสร้างพื้นฐานรอบๆ นั้นไม่ใช่เรื่องง่ายเลย ฐานข้อมูลหลักของ CobbleDB อย่างเดียวมีโค้ดภาษา Rust ประมาณ 40,000 บรรทัด และการย้ายข้อมูลยังต้องใช้ความพยายามอย่างมากในการตรวจสอบความถูกต้องและพฤติกรรมเมื่อเกิดความล้มเหลวในระดับการผลิต การบูรณาการ CobbleDB เข้ากับเส้นทางการประมวลผลและการให้บริการ และการเปลี่ยนผ่านทราฟฟิกจริงอย่างปลอดภัย

เราเร่งความพยายามนี้โดยใช้ระบบภายในที่ใช้งานกลุ่มของโค้ดดิ้งเอเจนต์ ระบบได้รับการออกแบบให้มีความคงทนสูง โดยจะรักษาเป้าหมายของโปรเจกต์ ความเสี่ยงที่ใช้งานอยู่ ประวัติของที่เก็บโค้ด และการตัดสินใจก่อนหน้าข้ามเซสชันต่างๆ เอาไว้

เราได้มอบหมายกลุ่มเอเจนต์เชิงรุกที่ทำงานตลอดเวลาเหล่านี้ให้กับโปรเจกต์ CobbleDB งานแรกของพวกเขาคือการสร้างแบบจำลองที่แม่นยำของงานที่กำลังดำเนินการอยู่ พวกเขาตรวจสอบช่องทางของโปรเจกต์และเธรดที่ใช้งานอยู่ เชื่อมโยงรายการที่ค้างอยู่กับ pull request และเจ้าของ และจัดทำรายงานปัจจุบันเกี่ยวกับสถานะสุขภาพของระบบและการดำเนินการถัดไป จากนั้นพวกเขาก็ทำงานในส่วนต่างๆ ของลูปวิศวกรรม:

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

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

บทสรุป

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

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

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

การอ้างอิง

BibTeX
@online{perplexity2026Cobbledb,
  author = {{Perplexity} and Alexandr Yarats and Marco Montagna and Aleksei Bykov and Alexey Gusev and Ali Abdullaev and Amir Mokhammed-Ali and Andrey Zosimov and Denis Kremko and Gleb Vishnevsky and Micah Zirn and Phi Hoang and Claire Beamer and Jeremy Yang and Jerry Ma and Denis Yarats},
  title = {{CobbleDB: Rebuilding AI search storage for lower latency and cost}},
  date = {2026-09-14},
  url = {https://www.perplexity.ai/hub/blog/cobbledb}
}