ล่าสุดผมบอกวิธีใช้สูตรมาตรฐานแล้ว
#1. การแบ่งส่วน
บทความเกี่ยวกับวิธีการและเหตุผลที่ควรจัดระเบียบ
“เรื่องต่างๆ ของวันเวลาที่ผ่านไป...”
ในขั้นต้น เช่นเดียวกับ MVP อื่น ๆ โครงการของเราเริ่มต้นภายใต้ภาระงานที่ค่อนข้างเบา - การตรวจสอบดำเนินการเฉพาะสำหรับเซิร์ฟเวอร์ที่สำคัญที่สุดสิบเครื่องเท่านั้น ตารางทั้งหมดมีขนาดค่อนข้างเล็ก... แต่เมื่อเวลาผ่านไป จำนวนโฮสต์ที่ได้รับการตรวจสอบก็เพิ่มมากขึ้นเรื่อย ๆ และอีกครั้งที่เราพยายามทำอะไรบางอย่างกับหนึ่งในนั้น ตารางขนาด 1.5TBเราตระหนักว่าถึงแม้จะเป็นไปได้ที่จะใช้ชีวิตแบบนี้ต่อไป แต่มันก็ไม่สะดวกมาก
ช่วงเวลานั้นเกือบจะเหมือนกับช่วงเวลาที่ยิ่งใหญ่ PostgreSQL 9.x เวอร์ชันต่างๆ มีความเกี่ยวข้องกัน ดังนั้นการแบ่งพาร์ติชั่นทั้งหมดจึงต้องดำเนินการ "ด้วยตนเอง" ผ่าน การสืบทอดตารางและทริกเกอร์ การกำหนดเส้นทางด้วยไดนามิก EXECUTE
.
ผลลัพธ์ที่ได้กลายเป็นสากลเพียงพอที่จะแปลเป็นตารางทั้งหมดได้:
- มีการประกาศตารางพาเรนต์ "ส่วนหัว" ที่ว่างเปล่า ซึ่งอธิบายทั้งหมด ดัชนีและทริกเกอร์ที่จำเป็น.
- บันทึกจากมุมมองของลูกค้าถูกสร้างขึ้นในตาราง "รูท" และใช้งานภายใน ทริกเกอร์การกำหนดเส้นทาง
BEFORE INSERT
บันทึกถูกแทรก "ทางกายภาพ" ลงในส่วนที่ต้องการ หากไม่มีสิ่งนั้น เราก็ได้รับข้อยกเว้นและ... - … โดยใช้
ถูกสร้างขึ้นตามเทมเพลตของตารางหลัก ส่วนที่มีข้อจำกัดเรื่องวันที่ต้องการเพื่อว่าเมื่อดึงข้อมูลแล้ว การอ่านจะดำเนินการเฉพาะในนั้นเท่านั้นCREATE TABLE ... (LIKE ... INCLUDING ...)
PG10: ความพยายามครั้งแรก
แต่ในอดีตการแบ่งพาร์ติชันผ่านการสืบทอดนั้นไม่เหมาะอย่างยิ่งในการจัดการกับสตรีมการเขียนที่ใช้งานอยู่หรือพาร์ติชันย่อยจำนวนมาก ตัวอย่างเช่นคุณสามารถจำได้ว่ามีอัลกอริธึมในการเลือกส่วนที่ต้องการ ความซับซ้อนกำลังสองว่ามันใช้งานได้กับ 100+ ส่วน คุณเองก็เข้าใจวิธีการ...
ใน PG10 สถานการณ์นี้ได้รับการปรับให้เหมาะสมอย่างมากโดยการใช้การสนับสนุน
ตามที่ปรากฎหลังจากขุดดูคู่มือ ตารางที่แบ่งพาร์ติชันในเวอร์ชันนี้คือ:
- ไม่รองรับคำอธิบายดัชนี
- ไม่รองรับทริกเกอร์
- ไม่สามารถเป็น “ผู้สืบเชื้อสาย” ของใครได้
- ไม่สนับสนุน
INSERT ... ON CONFLICT
- ไม่สามารถสร้างส่วนได้โดยอัตโนมัติ
เมื่อได้รับการชกที่หน้าผากอย่างเจ็บปวดด้วยคราดเราก็ตระหนักว่าคงเป็นไปไม่ได้หากไม่มีการแก้ไขแอปพลิเคชันและเลื่อนการวิจัยเพิ่มเติมออกไปเป็นเวลาหกเดือน
PG10: โอกาสครั้งที่สอง
ดังนั้นเราจึงเริ่มแก้ไขปัญหาที่เกิดขึ้นทีละประเด็น:
- เพราะสิ่งกระตุ้นและ
ON CONFLICT
เราพบว่าเรายังต้องการพวกมันอยู่ตรงนี้และตรงนั้น ดังนั้นเราจึงสร้างขั้นตอนกลางเพื่อจัดการพวกมัน ตารางพร็อกซี. - กำจัด "เส้นทาง" ในทริกเกอร์ - นั่นคือจาก
EXECUTE
. - พวกเขาแยกมันออกมา ตารางเทมเพลตพร้อมดัชนีทั้งหมดเพื่อไม่ให้ปรากฏอยู่ในตารางพร็อกซีด้วยซ้ำ
ในที่สุด หลังจากทั้งหมดนี้ เราก็แบ่งพาร์ติชันตารางหลักแบบเนทีฟ การสร้างส่วนใหม่ยังคงเหลืออยู่ในจิตสำนึกของแอปพลิเคชัน
พจนานุกรม "เลื่อย"
เช่นเดียวกับในระบบการวิเคราะห์อื่นๆ เราก็มีเช่นกัน "ข้อเท็จจริง" และ "จุดตัด" (พจนานุกรม) ในกรณีของเรา พวกเขาดำเนินการในฐานะนี้ เช่น
“ข้อเท็จจริง” ถูกแบ่งส่วนตามวันเป็นเวลานานแล้ว ดังนั้นเราจึงลบส่วนที่ล้าสมัยอย่างใจเย็น และพวกเขาไม่ได้รบกวนเรา (บันทึก!) แต่มีปัญหากับพจนานุกรม...
ไม่ได้บอกว่ามีเยอะแต่ก็ประมาณนี้ “ข้อเท็จจริง” จำนวน 100TB ส่งผลให้มีพจนานุกรมขนาด 2.5TB. คุณไม่สามารถลบสิ่งใดออกจากตารางได้อย่างสะดวก ไม่สามารถบีบอัดได้ในเวลาที่เหมาะสม และการเขียนลงในตารางจะค่อยๆ ช้าลง
เหมือนพจนานุกรม...ในนั้นควรนำเสนอแต่ละรายการเพียงครั้งเดียว...ซึ่งก็ถูกต้อง แต่!.. ไม่มีใครหยุดเราจากการมี พจนานุกรมแยกต่างหากในแต่ละวัน! ใช่ สิ่งนี้ทำให้เกิดความซ้ำซ้อน แต่จะช่วยให้:
- เขียน/อ่านเร็วขึ้น เนื่องจากขนาดส่วนเล็กลง
- ใช้หน่วยความจำน้อยลง โดยทำงานร่วมกับดัชนีที่มีขนาดกะทัดรัดมากขึ้น
- เก็บข้อมูลน้อยลง เนื่องจากสามารถลบล้าสมัยได้อย่างรวดเร็ว
อันเป็นผลมาจากมาตรการที่ซับซ้อนทั้งหมด โหลด CPU ลดลง ~30% โหลดดิสก์ประมาณ ~50%:
ในเวลาเดียวกัน เรายังคงเขียนสิ่งเดียวกันนี้ลงในฐานข้อมูลต่อไป โดยมีภาระงานน้อยลง
#2. วิวัฒนาการฐานข้อมูลและการปรับโครงสร้างใหม่
เราจึงตกลงกับสิ่งที่เรามี ในแต่ละวันมีส่วนของตัวเอง พร้อมข้อมูล จริงๆ แล้ว, CHECK (dt = '2018-10-12'::date)
— และมีคีย์การแบ่งพาร์ติชั่นและเงื่อนไขสำหรับเรคคอร์ดให้ตกไปอยู่ในส่วนเฉพาะ
เนื่องจากรายงานทั้งหมดในบริการของเราสร้างขึ้นในบริบทของวันที่ที่ระบุ ดัชนีสำหรับรายงานเหล่านั้นตั้งแต่ “เวลาที่ไม่แบ่งพาร์ติชัน” จึงเป็นทุกประเภท (เซิร์ฟเวอร์ วันที่, เทมเพลตแผน), (เซิร์ฟเวอร์ วันที่, โหนดแผน), (วันที่, คลาสข้อผิดพลาด, เซิร์ฟเวอร์)...
แต่ตอนนี้พวกเขาอาศัยอยู่ทุกส่วน สำเนาของคุณ แต่ละดัชนีดังกล่าว... และภายในแต่ละส่วน วันที่เป็นค่าคงที่... ปรากฎว่าตอนนี้เราอยู่ในดัชนีดังกล่าวแล้ว เพียงป้อนค่าคงที่ เป็นหนึ่งในฟิลด์ที่เพิ่มทั้งปริมาณและเวลาในการค้นหา แต่ไม่มีผลลัพธ์ใด ๆ พวกเขาทิ้งคราดไว้กับตัวเอง อุ๊ย...
ทิศทางของการเพิ่มประสิทธิภาพนั้นชัดเจน - เรียบง่าย ลบฟิลด์วันที่ออกจากดัชนีทั้งหมด บนโต๊ะที่แบ่งพาร์ติชัน เมื่อพิจารณาจากปริมาณของเรา กำไรก็ประมาณนั้น 1TB/สัปดาห์!
ตอนนี้เราทราบว่ายังคงต้องบันทึกเทราไบต์นี้อยู่ นั่นก็คือเรายัง ตอนนี้ดิสก์ควรโหลดน้อยลง! ภาพนี้แสดงให้เห็นอย่างชัดเจนถึงผลกระทบที่ได้รับจากการทำความสะอาดซึ่งเราทุ่มเทให้กับหนึ่งสัปดาห์:
#3. “การแพร่กระจาย” ภาระสูงสุด
ปัญหาใหญ่อย่างหนึ่งของระบบโหลดคือ การซิงโครไนซ์ซ้ำซ้อน การดำเนินการบางอย่างที่ไม่ต้องการมัน บางครั้ง “เพราะพวกเขาไม่สังเกตเห็น” บางครั้ง “วิธีนั้นง่ายกว่า” แต่ไม่ช้าก็เร็วคุณก็ต้องกำจัดมันทิ้งไป
ลองซูมเข้าที่ภาพก่อนหน้าแล้วดูว่าเรามีดิสก์ “ปั๊ม” ใต้โหลดที่มีแอมพลิจูดสองเท่า ระหว่างตัวอย่างที่อยู่ติดกัน ซึ่งชัดเจนว่า "ทางสถิติ" ไม่ควรเกิดขึ้นกับการดำเนินการจำนวนดังกล่าว:
มันค่อนข้างง่ายที่จะบรรลุผล เราได้เริ่มติดตามแล้ว เกือบ 1000 เซิร์ฟเวอร์แต่ละเธรดจะถูกประมวลผลโดยเธรดลอจิคัลที่แยกจากกัน และแต่ละเธรดจะรีเซ็ตข้อมูลที่สะสมเพื่อส่งไปยังฐานข้อมูลที่ความถี่ที่แน่นอน บางอย่างเช่นนี้:
setInterval(sendToDB, interval)
ปัญหาตรงนี้อยู่ที่ข้อเท็จจริงที่ว่า เธรดทั้งหมดเริ่มต้นในเวลาเดียวกันโดยประมาณดังนั้นเวลาในการส่งจึงมักจะตรงกัน "ตรงประเด็น" เสมอ อุ๊ย #2...
โชคดีที่มันค่อนข้างง่ายที่จะแก้ไข เพิ่มการวิ่งขึ้นแบบ "สุ่ม" ตามเวลา:
setInterval(sendToDB, interval * (1 + 0.1 * (Math.random() - 0.5)))
#4. เราแคชสิ่งที่เราต้องการ
ปัญหาการโหลดสูงแบบดั้งเดิมประการที่สามคือ ไม่มีแคช เขาอยู่ไหน สามารถ เป็น.
ตัวอย่างเช่น เราทำให้สามารถวิเคราะห์ในแง่ของโหนดแผนได้ (ทั้งหมดนี้ Seq Scan on users
) แต่คิดทันทีว่าส่วนใหญ่เหมือนกัน - พวกเขาลืมไป
ไม่ แน่นอนว่าไม่มีสิ่งใดถูกเขียนลงฐานข้อมูลอีก ซึ่งเป็นการตัดทริกเกอร์ด้วย INSERT ... ON CONFLICT DO NOTHING
. แต่ข้อมูลนี้ยังเข้าถึงฐานข้อมูลและไม่จำเป็น การอ่านเพื่อตรวจสอบความขัดแย้ง ต้องทำ. อุ๊ย#3...
ความแตกต่างในจำนวนบันทึกที่ส่งไปยังฐานข้อมูลก่อน/หลังการเปิดใช้งานแคชนั้นชัดเจน:
และนี่คือปริมาณการจัดเก็บข้อมูลที่ลดลงตามมา:
เบ็ดเสร็จ
“เทราไบต์ต่อวัน” ฟังดูน่ากลัวจริงๆ หากคุณทำทุกอย่างถูกต้องนี่ก็เป็นเพียง 2^40 ไบต์ / 86400 วินาที = ~12.5MB/วินาทีที่แม้แต่สกรู IDE บนเดสก์ท็อปก็ยังถืออยู่ 🙂
แต่จริงๆ แล้ว แม้ว่าภาระงานจะ "เอียง" ถึงสิบเท่าในระหว่างวัน คุณก็ยังสามารถตอบสนองความสามารถของ SSD สมัยใหม่ได้อย่างง่ายดาย
ที่มา: will.com