ปัจจุบัน Redis ได้กลายเป็นหนึ่งในโซลูชันแคชที่ได้รับความนิยมมากที่สุดในอุตสาหกรรมอินเทอร์เน็ต แม้ว่าระบบฐานข้อมูลเชิงสัมพันธ์ (SQL) จะนำคุณสมบัติที่ยอดเยี่ยมมากมาย เช่น ACID มาให้ แต่ประสิทธิภาพของฐานข้อมูลจะลดลงภายใต้ภาระงานสูงเพื่อรักษาคุณสมบัติเหล่านี้
เพื่อแก้ไขปัญหานี้ บริษัทและเว็บไซต์จำนวนมากได้ตัดสินใจเพิ่มเลเยอร์แคชระหว่างเลเยอร์แอปพลิเคชัน (เช่น โค้ดแบ็กเอนด์ที่จัดการกับตรรกะทางธุรกิจ) และเลเยอร์จัดเก็บข้อมูล (เช่น ฐานข้อมูล SQL) เลเยอร์แคชนี้มักจะถูกนำไปใช้โดยใช้แคชในหน่วยความจำ นี่เป็นเพราะว่า ตามที่ระบุไว้ในตำราเรียนหลายเล่ม คอขวดด้านประสิทธิภาพของฐานข้อมูล SQL แบบดั้งเดิมมักจะเป็น I/O ไปยังที่จัดเก็บข้อมูลสำรอง (เช่น ฮาร์ดดิสก์) เนื่องจากราคาของหน่วยความจำหลัก (RAM) ลดลงในช่วงทศวรรษที่ผ่านมา ตอนนี้จึงเป็นไปได้ที่จะจัดเก็บ (อย่างน้อยบางส่วนของ) ข้อมูลในหน่วยความจำหลักเพื่อปรับปรุงประสิทธิภาพ หนึ่งในตัวเลือกยอดนิยมคือ Redis
แน่นอนว่าระบบส่วนใหญ่จะจัดเก็บเฉพาะสิ่งที่เรียกว่า "ข้อมูลร้อน" ในเลเยอร์แคช (เช่น หน่วยความจำหลัก) นี่เป็นไปตามหลักการพาเรโต (หรือที่เรียกว่ากฎ 80/20) สำหรับเหตุการณ์หลายๆ อย่าง ผลกระทบประมาณ 80% มาจากสาเหตุ 20% เพื่อให้คุ้มค่า เราเพียงแค่ต้องจัดเก็บ 20% นั้นในเลเยอร์แคช ในการระบุ "ข้อมูลร้อน" เราสามารถระบุนโยบายการนำออก (เช่น LFU หรือ LRU) เพื่อกำหนดว่าข้อมูลใดจะหมดอายุ
ภูมิหลัง
ดังที่กล่าวไว้ก่อนหน้านี้ ข้อมูลบางส่วนจากฐานข้อมูล SQL จะถูกจัดเก็บไว้ในแคชในหน่วยความจำ เช่น Redis แม้ว่าประสิทธิภาพจะดีขึ้น แต่วิธีนี้ก็นำมาซึ่งปัญหาใหญ่ที่เราไม่มีแหล่งข้อมูลเดียวที่เป็นความจริงอีกต่อไป ตอนนี้ ข้อมูลเดียวกันจะถูกจัดเก็บไว้ในสองที่ เราจะมั่นใจได้อย่างไรถึงความสอดคล้องกันระหว่างข้อมูลที่จัดเก็บใน Redis และข้อมูลที่จัดเก็บในฐานข้อมูล SQL ในขณะที่ หลีกเลี่ยงการบล็อก
ด้านล่างนี้ เราจะนำเสนอข้อผิดพลาดทั่วไปบางประการและชี้ให้เห็นว่าอะไรที่อาจผิดพลาดได้ เรายังนำเสนอแนวทางแก้ไขปัญหาที่ซับซ้อนนี้อีกด้วย
โปรดทราบ: เพื่อให้การอภิปรายของเราง่ายขึ้น เราจะยกตัวอย่าง Redis และฐานข้อมูล SQL แบบดั้งเดิม อย่างไรก็ตาม โปรดทราบว่าแนวทางแก้ไขที่นำเสนอในโพสต์นี้สามารถขยายไปยังฐานข้อมูลอื่นๆ หรือแม้แต่ความสอดคล้องกันระหว่างสองเลเยอร์ใดๆ ในลำดับชั้นหน่วยความจำ
แนวทางแก้ไขต่างๆ
ด้านล่างนี้ เราจะอธิบายแนวทางแก้ไขปัญหานี้บางส่วน ส่วนใหญ่เป็นแนวทางที่เกือบจะถูกต้อง (แต่ก็ยังผิด) กล่าวอีกนัยหนึ่ง พวกเขาสามารถรับประกันความสอดคล้องกันระหว่าง 2 เลเยอร์ได้ 99.9% ของเวลา อย่างไรก็ตาม สิ่งต่างๆ อาจผิดพลาดได้ (เช่น ข้อมูลสกปรกในแคช) ภายใต้การทำงานพร้อมกันที่สูงมากและปริมาณการใช้งานจำนวนมาก
อย่างไรก็ตาม แนวทางแก้ไขที่เกือบจะถูกต้องเหล่านี้ถูกนำมาใช้อย่างมากในอุตสาหกรรม และหลายบริษัทได้ใช้แนวทางเหล่านี้มาหลายปีโดยไม่มีปัญหาใหญ่ บางครั้ง การเปลี่ยนจากความถูกต้อง 99.9% เป็นความถูกต้อง 100% นั้นเป็นเรื่องที่ท้าทายเกินไป สำหรับธุรกิจในโลกแห่งความเป็นจริง วงจรการพัฒนาที่รวดเร็วขึ้นและระยะเวลาในการออกสู่ตลาดที่สั้นลงอาจมีความสำคัญมากกว่า
การหมดอายุของแคช
โซลูชันที่เรียบง่ายบางอย่างพยายามใช้การหมดอายุของแคชหรือนโยบายการเก็บรักษาเพื่อจัดการความสอดคล้องกันระหว่าง MySQL และ Redis แม้ว่าโดยทั่วไปแล้วจะเป็นแนวทางปฏิบัติที่ดีในการกำหนดเวลาหมดอายุและนโยบายการเก็บรักษาสำหรับ Redis Cluster ของคุณอย่างระมัดระวัง แต่นี่เป็นโซลูชันที่แย่มากในการรับประกันความสอดคล้องกัน สมมติว่าเวลาหมดอายุของแคชของคุณคือ 30 นาที คุณแน่ใจหรือว่าคุณสามารถรับความเสี่ยงในการอ่านข้อมูลสกปรกได้นานถึงครึ่งชั่วโมง
แล้วการตั้งเวลาหมดอายุให้สั้นลงล่ะ? สมมติว่าเราตั้งไว้ที่ 1 นาที น่าเสียดายที่เรากำลังพูดถึงบริการที่มีปริมาณการใช้งานจำนวนมากและการทำงานพร้อมกันสูง 60 วินาทีอาจทำให้เราสูญเสียเงินหลายล้านดอลลาร์
อืม ลองตั้งให้สั้นลงอีกหน่อยล่ะ 5 วินาทีล่ะ? คุณได้ลดระยะเวลาที่ไม่สอดคล้องกันลงแล้ว อย่างไรก็ตาม คุณได้ทำลายวัตถุประสงค์เดิมของการใช้แคช! คุณจะมีการพลาดแคชจำนวนมาก และมีแนวโน้มว่าประสิทธิภาพของระบบจะลดลงอย่างมาก
Cache Aside
อัลกอริทึมสำหรับรูปแบบ cache aside คือ:
- สำหรับการดำเนินการที่ไม่เปลี่ยนแปลง (อ่าน):
- แคชฮิต: คืนค่าข้อมูลจาก Redis โดยตรง โดยไม่มีการสืบค้นไปยัง MySQL;
- แคชมิส: สั่งให้ MySQL เพื่อรับข้อมูล (สามารถใช้ read replicas เพื่อปรับปรุงประสิทธิภาพ), บันทึกข้อมูลที่ส่งคืนไปยัง Redis, คืนผลลัพธ์ให้กับไคลเอนต์
- สำหรับการดำเนินการที่เปลี่ยนแปลงได้ (สร้าง อัปเดต ลบ):
- สร้าง อัปเดต หรือลบข้อมูลไปยัง MySQL;
- ลบรายการใน Redis (ลบเสมอแทนที่จะอัปเดตแคช ค่าใหม่จะถูกแทรกเมื่อแคชมิสครั้งถัดไป)
แนวทางนี้ส่วนใหญ่จะใช้ได้กับกรณีการใช้งานทั่วไป ในความเป็นจริง cache aside เป็นมาตรฐานโดยพฤตินัยสำหรับการนำความสอดคล้องกันระหว่าง MySQL และ Redis มาใช้ เอกสารที่มีชื่อเสียง Scaling Memecache at Facebook ยังอธิบายถึงแนวทางดังกล่าวด้วย อย่างไรก็ตาม มีปัญหาบางอย่างกับแนวทางนี้เช่นกัน:
- ภายใต้สถานการณ์ปกติ (สมมติว่ากระบวนการไม่เคยถูกฆ่าและเขียนไปยัง MySQL/Redis จะไม่ล้มเหลว) ส่วนใหญ่สามารถรับประกันความสอดคล้องกันในที่สุดได้ สมมติว่ากระบวนการ A พยายามอัปเดตค่าที่มีอยู่ ในช่วงเวลาหนึ่ง A ได้อัปเดตค่าใน MySQL สำเร็จแล้ว ก่อนที่จะลบรายการใน Redis กระบวนการ B อื่นพยายามอ่านค่าเดียวกัน B จะได้รับแคชฮิต (เนื่องจากรายการยังไม่ได้ถูกลบใน Redis) ดังนั้น B จะอ่านค่าที่ล้าสมัย อย่างไรก็ตาม รายการเก่าใน Redis จะถูกลบในที่สุด และกระบวนการอื่นๆ จะได้รับค่าที่อัปเดตในที่สุด
- ภายใต้สถานการณ์ที่รุนแรง ก็ไม่สามารถรับประกันความสอดคล้องกันในที่สุดได้เช่นกัน ลองพิจารณาสถานการณ์เดียวกัน หากกระบวนการ A ถูกฆ่าก่อนที่จะพยายามลบรายการใน Redis รายการเก่าจะไม่ถูกลบ ดังนั้น กระบวนการอื่นๆ ทั้งหมดหลังจากนั้นจะอ่านค่าเก่าต่อไป
- แม้ภายใต้สถานการณ์ปกติ ก็ยังมีกรณีพิเศษที่มีความน่าจะเป็นต่ำมากที่ความสอดคล้องกันในที่สุดอาจแตกหัก สมมติว่ากระบวนการ C พยายามอ่านค่าและได้รับแคชมิส จากนั้น C สั่งให้ MySQL และได้รับผลลัพธ์ที่ส่งคืน ทันใดนั้น C ก็ติดขัดและถูก OS หยุดชั่วคราว ในขณะนี้ กระบวนการ D อื่นพยายามอัปเดตค่าเดียวกัน D อัปเดต MySQL และได้ลบรายการใน Redis หลังจากนั้น C กลับมาทำงานต่อและบันทึกผลการสืบค้นลงใน Redis ดังนั้น C จะบันทึกค่าเก่าลงใน Redis และกระบวนการต่อๆ ไปทั้งหมดจะอ่านข้อมูลสกปรก นี่อาจฟังดูน่ากลัว แต่ความน่าจะเป็นต่ำมากเพราะ:
- หาก D พยายามอัปเดตค่าที่มีอยู่ รายการนี้ควรมีอยู่ใน Redis เมื่อ C พยายามอ่าน กรณีนี้จะไม่เกิดขึ้นหาก C ได้รับแคชฮิต เพื่อให้เกิดกรณีดังกล่าว รายการนั้นจะต้องหมดอายุและถูกลบออกจาก Redis อย่างไรก็ตาม หากรายการนี้ "ร้อนมาก" (เช่น มีปริมาณการอ่านจำนวนมาก) ควรถูกบันทึกลงใน Redis อีกครั้งในไม่ช้าหลังจากหมดอายุ หากสิ่งนี้เป็นของ "ข้อมูลเย็น" ควรมีความสอดคล้องกันต่ำ และดังนั้นจึงไม่ค่อยมีคำขออ่านหนึ่งรายการและคำขออัปเดตหนึ่งรายการในรายการนี้พร้อมกัน
- ส่วนใหญ่ การเขียนไปยัง Redis ควรเร็วกว่าการเขียนไปยัง MySQL ในความเป็นจริง การดำเนินการเขียนของ C บน Redis ควรเกิดขึ้นเร็วกว่าการดำเนินการลบของ D บน Redis มาก
Cache Aside - Variant 1
อัลกอริทึมสำหรับ cache aside รูปแบบที่ 1 คือ:
- สำหรับการดำเนินการที่ไม่เปลี่ยนแปลง (อ่าน):
- แคชฮิต: คืนค่าข้อมูลจาก Redis โดยตรง โดยไม่มีการสืบค้นไปยัง MySQL;
- แคชมิส: สั่งให้ MySQL เพื่อรับข้อมูล (สามารถใช้ read replicas เพื่อปรับปรุงประสิทธิภาพ), บันทึกข้อมูลที่ส่งคืนไปยัง Redis, คืนผลลัพธ์ให้กับไคลเอนต์
- สำหรับการดำเนินการที่เปลี่ยนแปลงได้ (สร้าง อัปเดต ลบ):
- ลบรายการใน Redis;
- สร้าง อัปเดต หรือลบข้อมูลไปยัง MySQL
นี่อาจเป็นโซลูชันที่แย่มาก สมมติว่ากระบวนการ A พยายามอัปเดตค่าที่มีอยู่ ในช่วงเวลาหนึ่ง A ได้ลบรายการใน Redis สำเร็จแล้ว ก่อนที่ A จะอัปเดตค่าใน MySQL กระบวนการ B พยายามอ่านค่าเดียวกันและได้รับแคชมิส จากนั้น B สั่งให้ MySQL และบันทึกข้อมูลที่ส่งคืนไปยัง Redis โปรดทราบว่าข้อมูลใน MySQl ยังไม่ได้ถูกอัปเดตในขณะนี้ เนื่องจาก A จะไม่ลบรายการ Redis อีกในภายหลัง ค่าเก่าจะยังคงอยู่ใน Redis และการอ่านค่านี้ในครั้งต่อๆ ไปทั้งหมดจะผิด
จากการวิเคราะห์ข้างต้น สมมติว่าสภาวะที่รุนแรงจะไม่เกิดขึ้น ทั้งอัลกอริทึม cache aside ดั้งเดิมและรูปแบบที่ 1 ไม่สามารถรับประกันความสอดคล้องกันในที่สุดได้ในบางกรณี (เราเรียกกรณีดังกล่าวว่าเส้นทางที่ไม่น่าพอใจ) อย่างไรก็ตาม ความน่าจะเป็นของเส้นทางที่ไม่น่าพอใจสำหรับรูปแบบที่ 1 นั้นสูงกว่าของอัลกอริทึมดั้งเดิมมาก
Cache Aside - Variant 2
อัลกอริทึมสำหรับ cache aside รูปแบบที่ 2 คือ:
- สำหรับการดำเนินการที่ไม่เปลี่ยนแปลง (อ่าน):
- แคชฮิต: คืนค่าข้อมูลจาก Redis โดยตรง โดยไม่มีการสืบค้นไปยัง MySQL;
- แคชมิส: สั่งให้ MySQL เพื่อรับข้อมูล (สามารถใช้ read replicas เพื่อปรับปรุงประสิทธิภาพ), บันทึกข้อมูลที่ส่งคืนไปยัง Redis, คืนผลลัพธ์ให้กับไคลเอนต์
- สำหรับการดำเนินการที่เปลี่ยนแปลงได้ (สร้าง อัปเดต ลบ):
- สร้าง อัปเดต หรือลบข้อมูลไปยัง MySQL;
- สร้าง อัปเดต หรือลบรายการใน Redis
นี่ก็เป็นโซลูชันที่ไม่ดีเช่นกัน สมมติว่ามีสองกระบวนการ A และ B ทั้งคู่พยายามอัปเดตค่าที่มีอยู่ A อัปเดต MySQL ก่อน B อย่างไรก็ตาม B อัปเดตรายการ Redis ก่อน A ในที่สุด ค่าใน MySQL จะถูกอัปเดตโดย B อย่างไรก็ตาม ค่าใน Redis จะถูกอัปเดตโดย A ซึ่งจะทำให้เกิดความไม่สอดคล้องกัน
ในทำนองเดียวกัน ความน่าจะเป็นของเส้นทางที่ไม่น่าพอใจสำหรับรูปแบบที่ 2 นั้นสูงกว่าของแนวทางดั้งเดิมมาก
Read Through
อัลกอริทึมสำหรับรูปแบบ read through คือ:
- สำหรับการดำเนินการที่ไม่เปลี่ยนแปลง (อ่าน):
- ไคลเอนต์จะอ่านจากแคชเสมอ ไม่ว่าจะเป็นแคชฮิตหรือแคชมิสก็โปร่งใสสำหรับไคลเอนต์ หากเป็นแคชมิส แคชควรมีความสามารถในการดึงข้อมูลจากฐานข้อมูลโดยอัตโนมัติ
- สำหรับการดำเนินการที่เปลี่ยนแปลงได้ (สร้าง อัปเดต ลบ):
- กลยุทธ์นี้ไม่ได้จัดการกับการดำเนินการที่เปลี่ยนแปลงได้ ควรใช้ร่วมกับรูปแบบ write through (หรือ write behind)
ข้อเสียที่สำคัญของรูปแบบ read through คือเลเยอร์แคชจำนวนมากอาจไม่รองรับ ตัวอย่างเช่น Redis จะไม่สามารถดึงข้อมูลจาก MySQL ได้โดยอัตโนมัติ (เว้นแต่คุณจะเขียนปลั๊กอินสำหรับ Redis)
Write Through
อัลกอริทึมสำหรับรูปแบบ write through คือ:
- สำหรับการดำเนินการที่ไม่เปลี่ยนแปลง (อ่าน):
- กลยุทธ์นี้ไม่ได้จัดการกับการดำเนินการที่ไม่เปลี่ยนแปลง ควรใช้ร่วมกับรูปแบบ read through
- สำหรับการดำเนินการที่เปลี่ยนแปลงได้ (สร้าง อัปเดต ลบ):
- ไคลเอนต์เพียงแค่ต้องสร้าง อัปเดต หรือลบรายการใน Redis เลเยอร์แคชจะต้องซิงโครไนซ์การเปลี่ยนแปลงนี้กับ MySQL โดยอัตโนมัติ
ข้อเสียของรูปแบบ write through ก็ชัดเจนเช่นกัน ประการแรก เลเยอร์แคชจำนวนมากจะไม่รองรับสิ่งนี้โดยกำเนิด ประการที่สอง Redis เป็นแคชมากกว่า RDBMS ไม่ได้ออกแบบมาให้มีความยืดหยุ่น ดังนั้น การเปลี่ยนแปลงอาจสูญหายไปก่อนที่จะถูกจำลองไปยัง MySQL แม้ว่าตอนนี้ Redis จะรองรับเทคนิคการคงอยู่ เช่น RDB และ AOF แต่ก็ยังไม่แนะนำให้ใช้วิธีนี้
Write Behind
อัลกอริทึมสำหรับรูปแบบ write behind คือ:
- สำหรับการดำเนินการที่ไม่เปลี่ยนแปลง (อ่าน):
- กลยุทธ์นี้ไม่ได้จัดการกับการดำเนินการที่ไม่เปลี่ยนแปลง ควรใช้ร่วมกับรูปแบบ read through
- สำหรับการดำเนินการที่เปลี่ยนแปลงได้ (สร้าง อัปเดต ลบ):
- ไคลเอนต์เพียงแค่ต้องสร้าง อัปเดต หรือลบรายการใน Redis เลเยอร์แคชจะบันทึกการเปลี่ยนแปลงลงใน message queue และส่งคืนความสำเร็จให้กับไคลเอนต์ การเปลี่ยนแปลงจะถูกจำลองไปยัง MySQL แบบอะซิงโครนัส และอาจเกิดขึ้นหลังจากที่ Redis ส่งการตอบกลับสำเร็จไปยังไคลเอนต์
รูปแบบ write behind แตกต่างจาก write through เพราะจะจำลองการเปลี่ยนแปลงไปยัง MySQL แบบอะซิงโครนัส ซึ่งจะปรับปรุงปริมาณงานเพราะไคลเอนต์ไม่ต้องรอให้การจำลองเกิดขึ้น message queue ที่มีความทนทานสูงอาจเป็นตัวเลือกที่เป็นไปได้ Redis stream (รองรับตั้งแต่ Redis 5.0) อาจเป็นตัวเลือกที่ดี เพื่อปรับปรุงประสิทธิภาพเพิ่มเติม สามารถรวมการเปลี่ยนแปลงและอัปเดต MySQL เป็นชุด (เพื่อประหยัดจำนวนการสืบค้น)
ข้อเสียของรูปแบบ write behind ก็คล้ายกัน ประการแรก เลเยอร์แคชจำนวนมากไม่รองรับสิ่งนี้โดยกำเนิด ประการที่สอง message queue ที่ใช้ต้องเป็น FIFO (เข้าก่อนออกก่อน) มิฉะนั้น การอัปเดตไปยัง MySQL อาจไม่เป็นไปตามลำดับ และดังนั้นผลลัพธ์สุดท้ายอาจไม่ถูกต้อง
Double Delete
อัลกอริทึมสำหรับรูปแบบ double delete คือ:
- สำหรับการดำเนินการที่ไม่เปลี่ยนแปลง (อ่าน):
- แคชฮิต: คืนค่าข้อมูลจาก Redis โดยตรง โดยไม่มีการสืบค้นไปยัง MySQL;
- แคชมิส: สั่งให้ MySQL เพื่อรับข้อมูล (สามารถใช้ read replicas เพื่อปรับปรุงประสิทธิภาพ), บันทึกข้อมูลที่ส่งคืนไปยัง Redis, คืนผลลัพธ์ให้กับไคลเอนต์
- สำหรับการดำเนินการที่เปลี่ยนแปลงได้ (สร้าง อัปเดต ลบ):
- ลบรายการใน Redis;
- สร้าง อัปเดต หรือลบข้อมูลไปยัง MySQL;
- พักการทำงานชั่วขณะ (เช่น 500ms);
- ลบรายการใน Redis อีกครั้ง
แนวทางนี้รวมอัลกอริทึม cache aside ดั้งเดิมและรูปแบบที่ 1 เนื่องจากเป็นการปรับปรุงจากแนวทาง cache aside ดั้งเดิม เราจึงสามารถประกาศได้ว่าส่วนใหญ่รับประกันความสอดคล้องกันในที่สุดภายใต้สถานการณ์ปกติ ได้พยายามแก้ไขเส้นทางที่ไม่น่าพอใจของทั้งสองแนวทางแล้ว
ด้วยการหยุดกระบวนการชั่วคราวเป็นเวลา 500ms อัลกอริทึมจะถือว่ากระบวนการอ่านพร้อมกันทั้งหมดได้บันทึกค่าเก่าลงใน Redis แล้ว และดังนั้นการดำเนินการลบครั้งที่ 2 บน Redis จะล้างข้อมูลสกปรกทั้งหมด แม้ว่าจะมีกรณีพิเศษที่อัลกอริทึมนี้จะทำลายความสอดคล้องกันในที่สุด แต่ความน่าจะเป็นนั้นจะน้อยมาก
Write Behind - Variant
ในตอนท้าย เราจะนำเสนอแนวทางใหม่ที่นำเสนอโดยโครงการ canal ที่พัฒนาโดย Alibaba Group จากประเทศจีน
วิธีการใหม่นี้สามารถพิจารณาได้ว่าเป็นรูปแบบหนึ่งของอัลกอริทึม write behind อย่างไรก็ตาม จะทำการจำลองในทิศทางอื่น แทนที่จะจำลองการเปลี่ยนแปลงจาก Redis ไปยัง MySQL จะสมัครรับข้อมูล binlog ของ MySQL และจำลองไปยัง Redis ซึ่งให้ความทนทานและความสอดคล้องกันที่ดีกว่าอัลกอริทึมดั้งเดิมมาก เนื่องจาก binlog เป็นส่วนหนึ่งของเทคโนโลยี RDMS เราจึงสามารถถือได้ว่ามีความทนทานและยืดหยุ่นภายใต้ภัยพิบัติ สถาปัตยกรรมดังกล่าวก็ค่อนข้างสมบูรณ์เช่นกัน เนื่องจากถูกใช้เพื่อจำลองการเปลี่ยนแปลงระหว่าง MySQL master และ slaves
บทสรุป
โดยสรุป ไม่มีแนวทางใดข้างต้นที่สามารถรับประกันความสอดคล้องกันอย่างมากได้ ความสอดคล้องกันอย่างมากอาจไม่ใช่ข้อกำหนดที่เป็นจริงสำหรับความสอดคล้องกันระหว่าง Redis และ MySQL เช่นกัน เพื่อรับประกันความสอดคล้องกันอย่างมาก เราต้องใช้ ACID กับการดำเนินการทั้งหมด การทำเช่นนั้นจะลดประสิทธิภาพของเลเยอร์แคช ซึ่งจะทำลายวัตถุประสงค์ของเราในการใช้แคช Redis
อย่างไรก็ตาม แนวทางทั้งหมดข้างต้นได้พยายามที่จะบรรลุความสอดคล้องกันในที่สุด ซึ่งแนวทางสุดท้าย (นำเสนอโดย canal) เป็นแนวทางที่ดีที่สุด อัลกอริทึมบางส่วนข้างต้นเป็นการปรับปรุงอัลกอริทึมอื่นๆ เพื่ออธิบายลำดับชั้นของอัลกอริทึมเหล่านี้ จะมีการวาดแผนภาพต้นไม้ต่อไปนี้ ในแผนภาพ แต่ละโหนดโดยทั่วไปจะมีความสอดคล้องกันที่ดีกว่าลูกๆ (ถ้ามี)
เราสรุปได้ว่าจะมีข้อแลกเปลี่ยนระหว่างความถูกต้อง 100% และประสิทธิภาพเสมอ บางครั้ง ความถูกต้อง 99.9% ก็เพียงพอสำหรับการใช้งานจริงแล้ว ในการวิจัยในอนาคต เราขอเตือนว่าผู้คนควรจำไว้ว่าจะไม่ทำลายวัตถุประสงค์เดิมของหัวข้อ ตัวอย่างเช่น เราไม่สามารถเสียสละประสิทธิภาพเมื่อพูดถึงความสอดคล้องกันระหว่าง MySQL และ Redis
อ้างอิง
- Scaling Memcache at Facebook
- Improve Cache Consistency
- Why does Facebook Use Delete to Remove the Key-value Pair in Memcache Instead of Updating Memcache?
หมายเหตุ โพสต์นี้ ได้รับอนุญาตให้เผยแพร่ซ้ำที่นี่โดยผู้เขียนต้นฉบับ Yunpeng Niu นักศึกษา CS ที่ยอดเยี่ยมของ NUS หากต้องการอ่านโพสต์เพิ่มเติมจากเขา โปรดไปที่ https://yunpengn.github.io/blog/
Great article! really helped me out understanding a correct workflow :)