The Four Axes of Scaling (สี่แกนของการ Scaling)

ไม่มีวิธีที่ถูกต้องเพียงวิธีเดียวในการ scale ระบบ เพราะเทคนิคที่ใช้จะขึ้นอยู่กับประเภทของข้อจำกัดที่คุณมี เรามี scaling หลายประเภทที่นำมาใช้ได้เพื่อช่วยเรื่องประสิทธิภาพ ความทนทาน หรืออาจจะทั้งสองอย่าง โมเดลที่ผมมักใช้อธิบายประเภทของ scaling ที่แตกต่างกันคือ Scale Cube จาก The Art of Scalability , 1 ซึ่งแบ่ง scaling ออกเป็นสามหมวดหมู่ ซึ่งในบริบทของระบบคอมพิวเตอร์ครอบคลุม functional decomposition, horizontal duplication และ data partitioning คุณค่าของโมเดลนี้คือมันช่วยให้คุณเข้าใจว่าคุณสามารถ scale ระบบไปตามหนึ่ง สอง หรือทั้งสามแกนนี้ได้ ขึ้นอยู่กับความต้องการของคุณ อย่างไรก็ตาม โดยเฉพาะในโลกของ infrastructure ที่ virtualized ผมมักรู้สึกว่าโมเดลนี้ขาดแกนที่สี่คือ vertical scaling แม้ว่านั่นจะทำให้มันไม่เป็น cube อีกต่อไปก็ตาม ถึงกระนั้น ผมคิดว่ามันเป็นชุดกลไกที่มีประโยชน์สำหรับเราในการหาว่าจะ scale สถาปัตยกรรม microservice ของเราได้ดีที่สุดอย่างไร ก่อนที่เราจะดู scaling ประเภทเหล่านี้อย่างละเอียด พร้อมข้อดีข้อเสียของแต่ละแบบ มาสรุปสั้นๆ กันก่อน:

Vertical scaling

พูดง่ายๆ คือการหาเครื่องที่ใหญ่ขึ้น

Horizontal duplication

การมีหลายสิ่งที่สามารถทำงานเดียวกันได้

Data partitioning

การแบ่งงานตามคุณลักษณะของข้อมูล เช่น กลุ่มลูกค้า

Functional decomposition

การแยกงานตามประเภท เช่น การแยกเป็น microservice

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

Vertical Scaling (การ Scale ในแนวตั้ง)

การดำเนินการบางอย่างได้ประโยชน์จากพลังที่มากขึ้นเท่านั้น การหาเครื่องที่ใหญ่ขึ้นพร้อม CPU ที่เร็วขึ้นและ I/O ที่ดีขึ้นมักจะพัฒนา latency และ throughput ให้ดีขึ้น ทำให้คุณประมวลผลงานได้มากขึ้นในเวลาที่น้อยลง ดังนั้นถ้าแอปพลิเคชันของคุณไม่เร็วพอหรือรับมือ request ได้ไม่พอ ทำไมไม่หาเครื่องที่ใหญ่ขึ้นล่ะ?

ในกรณีของ FoodCo หนึ่งในความท้าทายที่บริษัทกำลังเผชิญคือ write contention ที่เพิ่มขึ้นบนฐานข้อมูล primary ของตน โดยปกติแล้ว vertical scaling เป็นตัวเลือกที่ใช้เป็นอันดับแรกในการ scale การเขียนบนฐานข้อมูลเชิงสัมพันธ์อย่างรวดเร็ว และแท้จริงแล้ว FoodCo ก็ได้อัปเกรด database infrastructure ของตนไปแล้วหลายครั้ง ปัญหาคือ FoodCo ได้ผลักดันสิ่งนี้ไปไกลเท่าที่จะทำได้อย่างสบายใจแล้วจริงๆ Vertical scaling ทำงานได้ดีมาหลายปี แต่เมื่อพิจารณาจากประมาณการการเติบโตของบริษัท แม้ FoodCo จะหาเครื่องที่ใหญ่ขึ้นได้ นั่นก็ไม่น่าจะแก้ปัญหาได้ในระยะยาว

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

อย่างไรก็ตาม การเปลี่ยนไปใช้ virtualization และการเกิดขึ้นของ public cloud ได้ช่วยเรื่อง scaling รูปแบบนี้อย่างมหาศาล

Implementation (การนำไปใช้)

วิธี implement จะแตกต่างกันไปขึ้นอยู่กับว่าคุณรันบน infrastructure ของใคร ถ้ารันบน virtualized infrastructure ของคุณเอง คุณอาจแค่ resize VM เพื่อใช้ฮาร์ดแวร์ที่อยู่เบื้องหลังมากขึ้น—นี่เป็นสิ่งที่ควรจะรวดเร็วและมีความเสี่ยงต่ำในการ implement ถ้า VM ใหญ่เท่าที่ฮาร์ดแวร์เบื้องหลังจะรับได้แล้ว ตัวเลือกนี้ก็แน่นอนว่าใช้ไม่ได้—คุณอาจต้องซื้อฮาร์ดแวร์เพิ่ม ในทำนองเดียวกัน ถ้าคุณรันบน bare metal server ของคุณเองและไม่มีฮาร์ดแวร์สำรองที่ใหญ่กว่าที่คุณกำลังใช้อยู่ คุณก็ต้องพิจารณาซื้อเครื่องเพิ่มอีกเช่นกัน

โดยทั่วไป ถ้าผมมาถึงจุดที่ต้องซื้อ infrastructure ใหม่เพื่อลอง vertical scaling เนื่องจากต้นทุน (และเวลา) ที่เพิ่มขึ้นเพื่อให้สิ่งนี้ส่งผล ผมอาจข้าม scaling รูปแบบนี้ไปก่อนและดูที่ horizontal duplication แทน ซึ่งเราจะพูดถึงต่อไป

แต่การเกิดขึ้นของ public cloud ก็ทำให้เราสามารถเช่าเครื่องที่ถูกจัดการเต็มรูปแบบผ่านผู้ให้บริการ public cloud ได้ง่ายๆ บนพื้นฐานรายชั่วโมง (และในบางกรณีแม้แต่ระยะเวลาที่สั้นกว่านั้น) ยิ่งไปกว่านั้น ผู้ให้บริการ cloud หลักๆ เสนอเครื่องหลากหลายประเภทสำหรับปัญหาแต่ละแบบ workload ของคุณเน้นหน่วยความจำมากกว่าไหม? ลอง ใช้ instance u-24tb1.metal ของ AWS ที่ให้หน่วยความจำ 24 TB (ใช่ครับ คุณอ่านไม่ผิด) ทีนี้ จำนวน workload ที่อาจต้องการหน่วยความจำมากขนาดนี้จริงๆ ดูเหมือนจะหายากมาก แต่คุณก็มีตัวเลือกนั้น คุณยังมีเครื่องที่ปรับแต่งเฉพาะสำหรับ I/O สูง, CPU หรือการใช้งาน GPU ด้วยเช่นกัน ถ้าโซลูชันที่มีอยู่ของคุณอยู่บน public cloud อยู่แล้ว นี่เป็น scaling รูปแบบที่ลองง่ายมาก จนถ้าคุณกำลังหาชัยชนะเร็วๆ มันแทบไม่ต้องคิดเลย

Key benefits (ประโยชน์หลัก)

บน virtualized infrastructure โดยเฉพาะบนผู้ให้บริการ public cloud การ implement scaling รูปแบบนี้จะ รวดเร็ว งานส่วนใหญ่เกี่ยวกับการ scale แอปพลิเคชันขึ้นอยู่กับการทดลอง—มีไอเดียเกี่ยวกับบางสิ่งที่สามารถพัฒนาระบบของคุณ ทำการเปลี่ยนแปลง และวัดผลกระทบ กิจกรรมที่รวดเร็วและมีความเสี่ยงต่ำในการลองนั้นคุ้มค่าเสมอที่จะทำตั้งแต่เนิ่นๆ และ vertical scaling ก็เข้าเกณฑ์นี้พอดี

ยังควรสังเกตด้วยว่า vertical scaling สามารถทำให้ scaling ประเภทอื่นทำได้ง่ายขึ้นด้วย ตัวอย่างที่เป็นรูปธรรมคือ การย้าย database infrastructure ของคุณไปยังเครื่องที่ใหญ่ขึ้นอาจช่วยให้มันสามารถโฮสต์ฐานข้อมูลที่แยกกันในระดับ logical สำหรับ microservice ที่สร้างขึ้นใหม่ ในฐานะส่วนหนึ่งของ functional decomposition

โค้ดหรือฐานข้อมูลของคุณไม่น่าจะต้องเปลี่ยนแปลงอะไรเพื่อใช้ประโยชน์จาก infrastructure เบื้องหลังที่ใหญ่ขึ้น โดยสมมติว่า operating system และ chipset ยังคงเหมือนเดิม แม้ว่าจะต้องเปลี่ยนแปลงแอปพลิเคชันของคุณเพื่อใช้ประโยชน์จากการเปลี่ยนแปลงฮาร์ดแวร์ การเปลี่ยนแปลงเหล่านั้นก็อาจจำกัดอยู่แค่สิ่งอย่างการเพิ่มปริมาณหน่วยความจำที่มีให้ runtime ของคุณผ่าน runtime flag

Limitations (ข้อจำกัด)

ขณะที่เรา scale up เครื่องที่เรารันอยู่ CPU ของเราส่วนใหญ่ไม่ได้เร็วขึ้นจริงๆ เราแค่มี core มากขึ้น นี่เป็นการเปลี่ยนแปลงในช่วง 5-10 ปีที่ผ่านมา แต่ก่อนแต่ละรุ่นของฮาร์ดแวร์ใหม่จะมอบการพัฒนาความเร็ว clock ของ CPU ครั้งใหญ่ หมายความว่าโปรแกรมของเรามีการพัฒนาประสิทธิภาพแบบก้าวกระโดด การพัฒนาความเร็ว clock ได้ชะลอตัวลงอย่างมาก และแทนที่เราจะได้ CPU core มากขึ้นเรื่อยๆ ให้ใช้แทน ปัญหาคือซอฟต์แวร์ของเราบ่อยครั้งไม่ได้ถูกเขียนขึ้นเพื่อใช้ประโยชน์จากฮาร์ดแวร์ multicore สิ่งนี้อาจหมายความว่าการย้ายแอปพลิเคชันของคุณจากระบบ 4 core ไปเป็น 8 core อาจให้การพัฒนาน้อยมากหรือแทบไม่มีเลย แม้ว่าระบบที่มีอยู่ของคุณจะถูกจำกัดโดย CPU ก็ตาม การเปลี่ยนโค้ดเพื่อใช้ประโยชน์จากฮาร์ดแวร์ multicore อาจเป็นงานใหญ่มาก และอาจต้องการการเปลี่ยนแปลงทั้งหมดใน programming idiom

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

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

Horizontal Duplication (การทำสำเนาในแนวนอน)

ด้วย horizontal duplication คุณทำสำเนาส่วนหนึ่งของระบบเพื่อรับมือ workload ที่มากขึ้น กลไกที่แน่ชัดนั้นแตกต่างกันไป—เราจะดูวิธี implement กันในไม่ช้า—แต่โดยพื้นฐานแล้ว horizontal duplication ต้องการให้คุณมีวิธีกระจายงานไปทั่วสำเนาเหล่านี้

เช่นเดียวกับ vertical scaling scaling ประเภทนี้อยู่ในฝั่งที่ง่ายกว่าของสเปกตรัม และมักเป็นหนึ่งในสิ่งที่ผมจะลองก่อน ถ้าระบบ monolithic ของคุณรับโหลดไม่ไหว ให้ spin up สำเนาหลายชุดของมันแล้วดูว่าช่วยไหม!

Implementations (การนำไปใช้)

บางทีรูปแบบที่ชัดเจนที่สุดของ horizontal duplication ที่นึกถึงคือการใช้ load balancer เพื่อกระจาย request ไปทั่วสำเนาหลายชุดของฟังก์ชันการทำงานของคุณ ตามที่เราเห็นใน Figure 13-1 ที่ซึ่งเรากำลัง load balance ข้าม instance หลายตัวของ microservice Catalog ของ MusicCorp ความสามารถของ load balancer แตกต่างกันไป แต่คุณจะคาดหวังว่าทั้งหมดมีกลไกในการกระจายโหลดข้าม node และตรวจจับเมื่อ node หนึ่งใช้งานไม่ได้และเอามันออกจาก pool ของ load balancer จากมุมมองของ consumer load balancer เป็นเรื่องของ implementation ที่โปร่งใสทั้งหมด—เราสามารถมองว่ามันเป็นส่วนหนึ่งของขอบเขต logical ของ microservice ในแง่นี้ ในอดีต load balancer มักถูกมองในแง่ของฮาร์ดแวร์เฉพาะทาง แต่สิ่งนี้ไม่ได้เป็นเรื่องปกติมานานแล้ว—แทนที่แล้ว การ load balancing มากขึ้นถูกทำในซอฟต์แวร์ บ่อยครั้งรันอยู่ฝั่ง client

bms2 1301

Figure 13-1. Microservice Catalog ที่ deploy เป็น instance หลายตัว พร้อม load balancer เพื่อกระจาย request

อีกตัวอย่างของ horizontal duplication อาจเป็น competing consumer pattern ตามที่อธิบายไว้ใน Enterprise Integration Patterns . 3 ใน Figure 13-2 เราเห็นเพลงใหม่ถูกอัปโหลดขึ้นไปที่ MusicCorp เพลงใหม่เหล่านี้ต้องถูก transcode เป็นไฟล์รูปแบบต่างๆ เพื่อใช้เป็นส่วนหนึ่งของบริการ streaming ใหม่ของ MusicCorp เรามี queue งานร่วมกันที่งานเหล่านี้ถูกใส่ไว้ และมีชุด instance ของ Song Transcoder ทั้งหมดที่ consume จาก queue—instance ต่างๆ แข่งขันกันเพื่อทำงาน เพื่อเพิ่ม throughput ของระบบ เราสามารถเพิ่มจำนวน instance ของ Song Transcoder

bms2 1302

Figure 13-2. การ transcode สำหรับ streaming ถูก scale ขึ้นโดยใช้ competing consumer pattern

ในกรณีของ FoodCo รูปแบบหนึ่งของ horizontal duplication ถูกใช้เพื่อลดโหลดการอ่านบนฐานข้อมูล primary ผ่านการใช้ read replica ตามที่เราเห็นใน Figure 13-3 สิ่งนี้ลดโหลดการอ่านบน primary database node ปลดปล่อยทรัพยากรเพื่อรับมือการเขียน และได้ผลอย่างมีประสิทธิภาพมาก เพราะโหลดส่วนใหญ่บนระบบหลักเป็น read-heavy การอ่านเหล่านี้สามารถถูกเบี่ยงเบนไปยัง read replica เหล่านี้ได้อย่างง่ายดาย และเป็นเรื่องปกติที่จะใช้ load balancer ข้าม read replica หลายตัว

การ routing ไปยังฐานข้อมูล primary หรือ read replica ถูกจัดการภายใน microservice มันโปร่งใสสำหรับ consumer ของ microservice นี้ว่า request ที่พวกเขาส่งไปจะไปที่ฐานข้อมูล primary หรือ read replica

bms2 1303

Figure 13-3. FoodCo ใช้ read replica เพื่อ scale traffic การอ่าน

Key benefits (ประโยชน์หลัก)

Horizontal duplication ค่อนข้างตรงไปตรงมา แอปพลิเคชันไม่ค่อยต้องถูกอัปเดต เพราะงานในการกระจายโหลดมักทำที่อื่นได้—ตัวอย่างเช่น ผ่าน queue ที่รันบน message broker หรืออาจจะใน load balancer ถ้า vertical scaling ไม่มีให้ผมใช้ scaling รูปแบบนี้มักเป็นสิ่งถัดไปที่ผมจะพิจารณา

โดยสมมติว่างานสามารถกระจายไปทั่วสำเนาได้อย่างง่ายดาย มันเป็นวิธีที่สง่างามในการกระจายโหลดและลด contention สำหรับทรัพยากร computing ดิบๆ

Limitations (ข้อจำกัด)

เช่นเดียวกับตัวเลือก scaling แทบทั้งหมดที่เราจะดู horizontal duplication ต้องการ infrastructure มากขึ้น ซึ่งแน่นอนว่าอาจมีต้นทุนสูงขึ้น มันยังอาจเป็นเครื่องมือที่ค่อนข้างหยาบด้วย—คุณอาจต้องรันสำเนาเต็มรูปแบบหลายชุดของแอปพลิเคชัน monolithic ของคุณ ตัวอย่างเช่น แม้ว่าจะมีแค่ส่วนหนึ่งของ monolith นั้นที่กำลังประสบปัญหา scaling จริงๆ

งานส่วนใหญ่ที่นี่คือการ implement กลไกกระจายโหลดของคุณ ซึ่งอาจมีตั้งแต่แบบง่ายๆ เช่น HTTP load balancing ไปจนถึงแบบซับซ้อนกว่า เช่น การใช้ message broker หรือการตั้งค่า database read replica คุณกำลังพึ่งพากลไกกระจายโหลดนี้ให้ทำงานของมัน—การเข้าใจว่ามันทำงานอย่างไรและข้อจำกัดใดๆ ของตัวเลือกเฉพาะของคุณจะเป็นกุญแจสำคัญ

บางระบบอาจมี requirement เพิ่มเติมสำหรับกลไกกระจายโหลด ตัวอย่างเช่น พวกเขาอาจต้องการให้ทุก request ที่เกี่ยวข้องกับ user session เดียวกันถูกส่งไปยัง replica เดียวกัน สิ่งนี้แก้ได้ด้วยการใช้ load balancer ที่ต้องการ sticky session load balancing แต่นั่นก็อาจจำกัดกลไกกระจายโหลดที่คุณสามารถพิจารณาได้ ควรสังเกตว่าระบบที่ต้องการ sticky load balancing แบบนี้มักเจอปัญหาอื่นๆ ด้วย และโดยทั่วไปแล้วผมจะหลีกเลี่ยงการสร้างระบบที่มี requirement แบบนี้

Data Partitioning (การแบ่งพาร์ทิชันข้อมูล)

เริ่มจาก scaling รูปแบบที่ง่ายกว่า ตอนนี้เรากำลังเข้าสู่ดินแดนที่ยากขึ้น Data partitioning ต้องการให้เรากระจายโหลดตามแง่มุมหนึ่งของข้อมูล—บางทีอาจกระจายโหลดตามผู้ใช้ เป็นต้น

Implementation (การนำไปใช้)

วิธีที่ data partitioning ทำงานคือเราเอา key ที่เกี่ยวข้องกับ workload มาใช้ function กับมัน และผลลัพธ์คือ partition (บางครั้งเรียกว่า shard) ที่เราจะกระจายงานไปให้ ใน Figure 13-4 เรามีสอง partition และ function ของเราค่อนข้างง่าย—เราส่ง request ไปยังฐานข้อมูลหนึ่งถ้านามสกุลขึ้นต้นด้วย A ถึง M หรือไปยังฐานข้อมูลอีกตัวถ้านามสกุลขึ้นต้นด้วย N ถึง Z ทีนี้ นี่จริงๆ แล้วเป็นตัวอย่างที่ไม่ดีของ partitioning algorithm (เราจะพูดถึงว่าทำไมในไม่ช้า) แต่หวังว่านี่จะตรงไปตรงมาพอที่จะแสดงไอเดีย

bms2 1304

Figure 13-4. ข้อมูลลูกค้าถูกแบ่งพาร์ทิชันข้ามฐานข้อมูลสองตัวที่แตกต่างกัน

ในตัวอย่างนี้ เรากำลังแบ่งพาร์ทิชันในระดับฐานข้อมูล request ไปยัง microservice Customer สามารถไปที่ microservice instance ใดก็ได้ แต่เมื่อเราทำการดำเนินการที่ต้องใช้ฐานข้อมูล (อ่านหรือเขียน) request นั้นจะถูกส่งไปยัง database node ที่เหมาะสมตามชื่อของลูกค้า ในกรณีของฐานข้อมูลเชิงสัมพันธ์ schema ของ database node ทั้งสองจะเหมือนกัน แต่เนื้อหาของแต่ละตัวจะใช้ได้กับกลุ่มย่อยของลูกค้าเท่านั้น

การแบ่งพาร์ทิชันในระดับฐานข้อมูลมักสมเหตุสมผลถ้าเทคโนโลยีฐานข้อมูลที่คุณใช้รองรับแนวคิดนี้โดยธรรมชาติ เพราะคุณสามารถโยนปัญหานี้ไปให้ implementation ที่มีอยู่แล้วจัดการได้ อย่างไรก็ตาม เราสามารถแบ่งพาร์ทิชันที่ระดับ microservice instance แทนได้ ตามที่เราเห็นใน Figure 13-5 ในที่นี้ เราต้องสามารถหาได้จาก inbound request ว่า request นั้นควรถูก map ไปยัง partition ไหน—ในตัวอย่างของเรา สิ่งนี้ทำผ่าน proxy รูปแบบหนึ่ง ในกรณีของโมเดล partitioning ตามลูกค้าของเรา ถ้าชื่อของลูกค้าอยู่ใน request header นั่นก็เพียงพอแล้ว วิธีนี้สมเหตุสมผลถ้าคุณต้องการ microservice instance เฉพาะสำหรับ partitioning ซึ่งอาจมีประโยชน์ถ้าคุณกำลังใช้ in-memory caching มันยังหมายความว่าคุณสามารถ scale แต่ละ partition ได้ทั้งในระดับฐานข้อมูลและระดับ microservice instance

bms2 1305

Figure 13-5. Request ถูกส่งไปยัง microservice instance ที่เหมาะสม

เช่นเดียวกับตัวอย่าง read replica เราจะต้องการให้ scaling นี้ทำในลักษณะที่ consumer ของ microservice ไม่รู้ตัวถึงรายละเอียด implementation นี้ เมื่อ consumer ทำ request ไปยัง microservice Customer ใน Figure 13-5 เราต้องการให้ request ของพวกเขาถูก route ไปยัง partition ที่ถูกต้องแบบไดนามิก ความจริงที่ว่าเรา implement data partitioning ควรถูกมองว่าเป็นรายละเอียด implementation ภายในของ microservice ที่เกี่ยวข้อง—นี่ให้อิสระแก่เราในการเปลี่ยนแผน partitioning หรือแม้แต่ยกเลิก partitioning ไปเลย

อีกตัวอย่างทั่วไปของ data partitioning คือการทำตามพื้นฐานทางภูมิศาสตร์ คุณอาจมีหนึ่ง partition ต่อประเทศ หรือหนึ่งต่อภูมิภาค

สำหรับ FoodCo ตัวเลือกหนึ่งในการรับมือ contention บนฐานข้อมูล primary คือการแบ่งพาร์ทิชันข้อมูลตามประเทศ ดังนั้นลูกค้าในกานาไปที่ฐานข้อมูลหนึ่ง และลูกค้าในเจอร์ซีย์ไปที่อีกฐานข้อมูลหนึ่ง โมเดลนี้จะไม่สมเหตุสมผลสำหรับ FoodCo เนื่องจากปัจจัยหลายอย่าง ปัญหาหลักคือ FoodCo มีแผนที่จะขยายทางภูมิศาสตร์ต่อไป และหวังที่จะขับเคลื่อนประสิทธิภาพด้วยการให้บริการหลายพื้นที่ทางภูมิศาสตร์จากระบบเดียวกัน ไอเดียของการต้อง spin up partition ใหม่อย่างต่อเนื่องสำหรับแต่ละประเทศจะเพิ่มต้นทุนของการขยายเข้าสู่ประเทศใหม่อย่างมาก

บ่อยครั้ง partitioning จะถูกทำโดย subsystem ที่คุณพึ่งพา ตัวอย่างเช่น Cassandra ใช้ partition เพื่อกระจายทั้งการอ่านและการเขียนข้าม node ใน "ring" ที่กำหนด และ Kafka รองรับการกระจายข้อความข้าม partitioned topic

Key benefits (ประโยชน์หลัก)

Data partitioning scale ได้ดีมากสำหรับ workload แบบ transactional ถ้าระบบของคุณถูกจำกัดด้วยการเขียน เป็นต้น data partitioning สามารถมอบการพัฒนาอย่างมหาศาลได้

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

Data partitioning สามารถทำงานได้ดีร่วมกับ horizontal duplication—แต่ละ partition อาจประกอบด้วย node หลายตัวที่สามารถรับมืองานนั้นได้

Limitations (ข้อจำกัด)

ควรชี้ให้เห็นว่า data partitioning มีประโยชน์จำกัดในแง่ของการพัฒนาความทนทานของระบบ ถ้า partition หนึ่งล้มเหลว ส่วนนั้นของ request ของคุณจะล้มเหลว ตัวอย่างเช่น ถ้าโหลดของคุณกระจายเท่าๆ กันข้ามสี่ partition และหนึ่ง partition ล้มเหลว request ของคุณ 25% จะล้มเหลว นี่ไม่แย่เท่าความล้มเหลวทั้งหมด แต่ก็ยังค่อนข้างแย่อยู่ดี นี่คือเหตุผลที่ ตามที่กล่าวไว้ก่อนหน้านี้ เป็นเรื่องปกติที่จะรวม data partitioning เข้ากับเทคนิคอย่าง horizontal duplication เพื่อพัฒนาความทนทานของ partition ที่กำหนด

การได้ partition key ที่ถูกต้องอาจเป็นเรื่องยาก ใน Figure 13-5 เราใช้แผน partitioning ที่ค่อนข้างง่าย ที่เราแบ่งพาร์ทิชัน workload ตามนามสกุลของลูกค้า ลูกค้าที่มีนามสกุลขึ้นต้นด้วย A-M ไปที่ partition 1 และลูกค้าที่มีชื่อขึ้นต้นด้วย N-Z ไปที่ partition 2 ตามที่ผมชี้ให้เห็นตอนที่แชร์ตัวอย่างนั้น นี่ไม่ใช่กลยุทธ์ partitioning ที่ดี ด้วย data partitioning เราต้องการการกระจายโหลดที่เท่าเทียมกัน แต่เราคาดหวังการกระจายที่เท่าเทียมกันจากแผนที่ผมได้ระบุไว้ไม่ได้ ในประเทศจีน เป็นต้น ในอดีตมีนามสกุลจำนวนน้อยมาก และแม้แต่ทุกวันนี้ก็มีการประมาณว่ามีน้อยกว่า 4,000 นามสกุล นามสกุลยอดนิยม 100 อันดับแรก ซึ่งครอบคลุมกว่า 80% ของประชากร มักเอียงไปทางนามสกุลที่ขึ้นต้นด้วย N-Z ในภาษาจีนกลาง นี่เป็นตัวอย่างของแผน scaling ที่ไม่น่าจะให้การกระจายโหลดที่เท่าเทียมกัน และข้ามประเทศและวัฒนธรรมที่แตกต่างกันอาจให้ผลลัพธ์ที่แตกต่างกันอย่างมาก

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

การเพิ่ม partition ใหม่เข้าไปในแผนที่มีอยู่มักทำได้โดยไม่ยุ่งยากมากนัก ตัวอย่างเช่น การเพิ่ม node ใหม่เข้าไปใน Cassandra ring ไม่ต้องการการ rebalance ข้อมูลด้วยตัวเองเลย แทนที่แล้ว Cassandra มีการรองรับในตัวสำหรับการกระจายข้อมูลข้าม node แบบไดนามิก Kafka ก็ทำให้การเพิ่ม partition ใหม่ในภายหลังค่อนข้างง่ายเช่นกัน แม้ว่าข้อความที่อยู่ใน partition แล้วจะไม่ย้าย—แต่ producer และ consumer สามารถถูกแจ้งเตือนแบบไดนามิกได้

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

เรายังอาจเจอปัญหากับ query การหา record เดี่ยวๆ นั้นง่าย เพราะผมสามารถแค่ apply hashing function เพื่อหาว่า instance ไหนที่ข้อมูลควรอยู่ แล้วดึงมันจาก shard ที่ถูกต้อง แต่แล้ว query ที่ครอบคลุมข้อมูลใน node หลายตัวล่ะ—ตัวอย่างเช่น การหาลูกค้าทั้งหมดที่อายุเกิน 18 ปี ถ้าคุณต้องการ query shard ทั้งหมด คุณต้อง query แต่ละ shard แยกกันแล้ว join ในหน่วยความจำ หรือไม่ก็ต้องมี read store ทางเลือกที่ทั้งสองชุดข้อมูลพร้อมใช้งาน บ่อยครั้งการ query ข้าม shard ถูกจัดการโดยกลไก asynchronous โดยใช้ผลลัพธ์ที่ cache ไว้ Mongo ใช้ map/reduce job เช่น เพื่อทำ query เหล่านี้

ตามที่คุณอาจสรุปได้จากภาพรวมสั้นๆ นี้ การ scale ฐานข้อมูลสำหรับการเขียนคือจุดที่สิ่งต่างๆ ยากมาก และเป็นจุดที่ความสามารถของฐานข้อมูลต่างๆ เริ่มแตกต่างกันจริงๆ ผมมักเห็นคนเปลี่ยนเทคโนโลยีฐานข้อมูลเมื่อพวกเขาเริ่มเจอขีดจำกัดในการ scale ปริมาณการเขียนที่มีอยู่ได้อย่างง่ายดาย ถ้าสิ่งนี้เกิดขึ้น กับคุณ การซื้อเครื่องที่ใหญ่ขึ้นมักเป็นวิธีที่เร็วที่สุดในการแก้ ปัญหา แต่ในเบื้องหลังคุณอาจต้องการดูฐานข้อมูลประเภทอื่นที่อาจรองรับ requirement ของคุณได้ดีกว่า ด้วยฐานข้อมูลหลากหลายประเภทที่มีให้เลือก การเลือกฐานข้อมูลใหม่อาจเป็นกิจกรรมที่น่าหวาดหวั่น แต่เป็นจุดเริ่มต้น ผมขอแนะนำหนังสือที่กระชับอย่างน่าพอใจอย่าง NoSQL Distilled , 4 ซึ่งให้ภาพรวมของสไตล์ต่างๆ ของฐานข้อมูล NoSQL ที่มีให้คุณเลือก—ตั้งแต่ store ที่เชิงสัมพันธ์สูงอย่าง graph database ไปจนถึง document store, column store และ key-value store

โดยพื้นฐานแล้ว data partitioning เป็นงานที่มากขึ้น โดยเฉพาะเมื่อมันอาจต้องการการเปลี่ยนแปลงข้อมูลของระบบที่มีอยู่ของคุณอย่างกว้างขวาง อย่างไรก็ตาม โค้ดแอปพลิเคชันน่าจะได้รับผลกระทบเพียงเล็กน้อยเท่านั้น

Functional Decomposition (การแยกส่วนตามฟังก์ชัน)

ด้วย functional decomposition คุณดึงฟังก์ชันการทำงานออกมาและอนุญาตให้มันถูก scale แยกต่างหาก การดึงฟังก์ชันการทำงานออกจากระบบที่มีอยู่และสร้าง microservice ใหม่แทบจะเป็นตัวอย่างมาตรฐานของ functional decomposition ใน Figure 13-6 เราเห็นตัวอย่างจาก MusicCorp ที่ฟังก์ชันการทำงานของ order ถูกดึงออกจากระบบหลักเพื่อให้เราสามารถ scale ฟังก์ชันนี้แยกจากส่วนที่เหลือ

bms2 1306

Figure 13-6. Microservice Order ถูกดึงออกจากระบบ MusicCorp ที่มีอยู่

ในกรณีของ FoodCo นี่คือทิศทางข้างหน้าของบริษัท บริษัทได้ใช้ vertical scaling จนหมดแล้ว ใช้ horizontal duplication เท่าที่จะทำได้ ตัดสิทธิ์ data partitioning ไปแล้ว—สิ่งที่เหลืออยู่คือการเริ่มมุ่งไปสู่ functional decomposition ข้อมูลและ workload สำคัญกำลังถูกย้ายออกจากระบบหลักและฐานข้อมูลหลักเพื่อให้การเปลี่ยนแปลงนี้เกิดขึ้นได้ มีชัยชนะเร็วๆ ไม่กี่อย่างที่ถูกระบุ รวมถึงข้อมูลที่เกี่ยวข้องกับการจัดส่งและเมนูถูกย้ายออกจากฐานข้อมูลหลักไปยัง microservice เฉพาะ สิ่งนี้มีประโยชน์เพิ่มเติมคือมันสร้างโอกาสให้ทีม delivery ที่กำลังเติบโตของ FoodCo เริ่มจัดโครงสร้างตัวเองรอบความเป็นเจ้าของของ microservice ใหม่เหล่านี้

Implementation (การนำไปใช้)

ผมจะไม่พูดถึงกลไก scaling นี้มากเกินไป เพราะเราได้ครอบคลุมพื้นฐานของ microservice อย่างละเอียดแล้วในหนังสือเล่มนี้ สำหรับการอภิปรายที่ละเอียดกว่าเกี่ยวกับวิธีที่เราทำให้การเปลี่ยนแปลงแบบนี้เกิดขึ้นได้ ดู Chapter 3

Key benefits (ประโยชน์หลัก)

ความจริงที่ว่าเราได้แยก workload ประเภทต่างๆ ออกจากกันหมายความว่าตอนนี้เราสามารถปรับขนาด infrastructure เบื้องหลังที่จำเป็นสำหรับระบบของเราได้อย่างเหมาะสม ฟังก์ชันการทำงานที่แยกออกมาแล้วใช้งานเป็นครั้งคราวสามารถถูกปิดเมื่อไม่ต้องการ ฟังก์ชันการทำงานที่มี requirement โหลดพอประมาณสามารถ deploy บนเครื่องเล็กได้ ในทางกลับกัน ฟังก์ชันการทำงานที่กำลังถูกจำกัดในปัจจุบันสามารถได้รับฮาร์ดแวร์เพิ่มมากขึ้น บางทีอาจรวม functional decomposition เข้ากับหนึ่งใน scaling axis อื่นๆ—เช่น การรัน microservice ของเราหลายสำเนา

ความสามารถในการปรับขนาด infrastructure ที่จำเป็นในการรัน workload เหล่านี้ให้เราความยืดหยุ่นมากขึ้นในการปรับต้นทุนของ infrastructure ที่เราต้องการเพื่อรันระบบ นี่เป็นเหตุผลสำคัญว่าทำไมผู้ให้บริการ SaaS ขนาดใหญ่ใช้ microservice อย่างหนักมาก เพราะความสามารถในการหาสมดุลที่เหมาะสมของต้นทุน infrastructure สามารถช่วยขับเคลื่อนความสามารถในการทำกำไรได้

ด้วยตัวมันเอง functional decomposition ไม่ได้ทำให้ระบบของเราทนทานขึ้น แต่อย่างน้อยมันก็เปิดโอกาสให้เราสร้างระบบที่สามารถทนต่อความล้มเหลวบางส่วนของฟังก์ชันการทำงานได้ ซึ่งเป็นสิ่งที่เราสำรวจอย่างละเอียดกว่านี้ใน Chapter 12

โดยสมมติว่าคุณเลือกเส้นทาง microservice สำหรับ functional decomposition คุณจะมีโอกาสมากขึ้นในการใช้เทคโนโลยีที่แตกต่างกันซึ่งสามารถ scale microservice ที่แยกออกมาได้ ตัวอย่างเช่น คุณอาจย้ายฟังก์ชันการทำงานไปยังภาษาโปรแกรมและ runtime ที่มีประสิทธิภาพมากกว่าสำหรับประเภทงานที่คุณกำลังทำ หรือบางทีคุณอาจ migrate ข้อมูลไปยังฐานข้อมูลที่เหมาะกับ traffic การอ่านหรือเขียนของคุณมากกว่า

แม้ว่าในบทนี้เราจะเน้นไปที่ scale ในบริบทของระบบซอฟต์แวร์ของเราเป็นหลัก functional decomposition ยังทำให้การ scale องค์กรของเราง่ายขึ้นด้วย ซึ่งเป็นหัวข้อที่เราจะกลับมาพูดถึงใน Chapter 15

Limitations (ข้อจำกัด)

ตามที่เราสำรวจอย่างละเอียดใน Chapter 3 การแยกฟังก์ชันการทำงานออกจากกันอาจเป็นกิจกรรมที่ซับซ้อน และไม่น่าจะให้ประโยชน์ในระยะสั้น จาก scaling ทุกรูปแบบที่เราดูมา นี่คือรูปแบบที่น่าจะมีผลกระทบใหญ่ที่สุดต่อโค้ดแอปพลิเคชันของคุณ—ทั้งฝั่ง frontend และ backend มันยังอาจต้องการงานจำนวนมากใน data tier ถ้าคุณเลือกที่จะย้ายไปใช้ microservice ด้วย

คุณจะจบลงด้วยการเพิ่มจำนวน microservice ที่คุณกำลังรัน ซึ่งจะเพิ่มความซับซ้อนโดยรวมของระบบ—อาจนำไปสู่สิ่งที่ต้องบำรุงรักษา ทำให้ทนทาน และ scale มากขึ้น โดยทั่วไป เมื่อพูดถึงการ scale ระบบ ผมพยายามใช้ตัวเลือกอื่นๆ ให้หมดก่อนที่จะพิจารณา functional decomposition มุมมองของผมในเรื่องนี้อาจเปลี่ยนถ้าการเปลี่ยนไปใช้ microservice อาจนำสิ่งอื่นๆ จำนวนมากที่องค์กรกำลังมองหามาด้วย ในกรณีของ FoodCo เป็นต้น แรงขับเคลื่อนของบริษัทในการเติบโตทีม development ทั้งเพื่อรองรับประเทศมากขึ้นและส่งมอบฟีเจอร์มากขึ้นเป็นสิ่งสำคัญ ดังนั้นการ migrate ไปสู่ microservice จึงให้โอกาสบริษัทในการแก้ไม่เพียงแค่ปัญหา scaling ของระบบบางส่วน แต่ยังรวมถึงปัญหา scaling ขององค์กรด้วย