12 คะแนน โดย xguru 2024-09-26 | 7 ความคิดเห็น | แชร์ทาง WhatsApp
  • Wafris เป็นบริษัทไฟร์วอลล์เว็บแอปพลิเคชันโอเพนซอร์สที่มีไคลเอนต์แบบ Rails middleware
  • ไคลเอนต์ v1 เดิมต้องใช้ที่เก็บข้อมูล Redis แบบโลคัล แต่ใน v2 เปลี่ยนมาใช้ SQLite
  • อธิบายเบื้องหลังการตัดสินใจย้ายจาก Redis ไป SQLite รวมถึงประเด็นด้านประสิทธิภาพและการเปลี่ยนแปลงสถาปัตยกรรม

TL;DR

  • SQLite มีทั้งสิ่งที่ทำได้ดีและสิ่งที่ทำได้ไม่ดี
  • Redis ก็มีทั้งสิ่งที่ทำได้ดีและสิ่งที่ทำได้ไม่ดี
  • RDBMS แบบดั้งเดิม (Postgres/MySQL) ก็มีทั้งสิ่งที่ทำได้ดีและสิ่งที่ทำได้ไม่ดี
  • ระบบเก็บข้อมูลเหล่านี้ไม่สามารถสลับแทนกันได้โดยตรง และถ้าพยายามทำเช่นนั้นมักจะเจอปัญหา
  • บทความนี้อธิบายกระบวนการทดสอบและการตัดสินใจระหว่างการปรับสถาปัตยกรรมไคลเอนต์ v1 ที่ใช้ Redis ไปเป็นไคลเอนต์ v2 ที่ใช้ SQLite

ปัจจัยที่บีบให้ต้องเปลี่ยน

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

"ความเร็ว" คืออะไร?

  • Redis "เร็วกว่า" RDBMS แบบดั้งเดิม แต่ก็ยังต้องจัดการเรื่องการเชื่อมต่อ หน่วยความจำ โปรเซส และอื่น ๆ
  • ในสภาพแวดล้อมคลาวด์ ความหน่วงเครือข่ายอาจเป็นปัญหาใหญ่
  • ทุกคำขอ HTTP ที่เข้ามาต้องถูกประเมินตามกฎของ Wafris ดังนั้นความหน่วงเครือข่ายอาจทำให้แอปพลิเคชันช้าลง

สมมติฐานแบบโมโนลิทิก (Monolith-ish)

  • แม้จะมีแอปแบบกระจายเต็มรูปแบบอยู่บ้าง แต่แอป Rails ส่วนใหญ่เป็น "Majestic Monoliths"
  • แอปที่ดีพลอยข้ามหลายพื้นที่ แบ่งเซิร์ฟเวอร์ที่มีฟังก์ชันทับซ้อนกัน หรือเป็น Rails เพียงบางส่วน จะมีปัญหามากขึ้นเมื่อใช้ Redis

ทบทวนสถาปัตยกรรมใหม่

  • Wafris เป็นเว็บแอปพลิเคชันไฟร์วอลล์ที่ติดตั้งในรูปแบบ Rails middleware
  • หากแบ่งอย่างง่าย จะมี 2 ขั้นตอนคือ 1) เปรียบเทียบคำขอ HTTP กับกฎ และ 2) รายงานผลการประมวลผล
  • การ "อ่าน" กฎ (ขั้นตอนที่ 1) สำคัญกว่าการ "เขียน" (ขั้นตอนที่ 2) มาก
  • การอ่านต้องประมวลผลแบบลำดับ ต้องห้ามล้มเหลว และมีผลต่อประสิทธิภาพที่ผู้ใช้รับรู้
  • ส่วนการเขียนสามารถช้ากว่าได้ ทำแบบแบตช์ได้ หรือทำแบบอะซิงโครนัสได้

เข้าสู่ SQLite

  • มีคนอื่นอธิบายไว้ดีแล้วว่า SQLite เหมาะกับงานแบบใด
  • SQLite ไม่ได้แข่งกับฐานข้อมูลแบบ client/server แต่แข่งกับ fopen()
  • หากตัด network round-trip ออกไป ก็คาดว่าจะเร็วกว่า Redis มาก
  • จึงตัดสินใจทำการประเมิน benchmark ระหว่าง SQLite และ Redis

Benchmark ระหว่าง SQLite และ Redis

  • การทำ benchmark เป็นศาสตร์มืดที่ทำให้เราหลอกตัวเองด้วยตัวเลขที่ดูแม่นยำ
  • การ benchmark ระบบเก็บข้อมูลยิ่งยากเข้าไปอีก
  • เป้าหมายไม่ใช่หาความเร็วสัมบูรณ์ แต่สร้าง benchmark ที่เฉพาะกับข้อมูลและ use case ของเรา
  • มองข้ามการปรับจูนเพื่อเพิ่มประสิทธิภาพ เพราะต้องการให้ Wafris ใส่เข้าแอปแล้วทำงานได้ทันที
  • เราทดสอบเส้นทางหลักและคิวรีที่แย่ที่สุดของแอปจริง ไม่ใช่ benchmark เชิงทฤษฎี
  • คำขอไปยังโครงสร้างข้อมูล "lexical decimal" ที่ซับซ้อนซึ่งแมปช่วง IP (IPv4, IPv6) ไปยังหมวดหมู่ เป็นคิวรีที่แย่ที่สุด
  • คำนวณการค้นหาแบบช่วงล่วงหน้าแล้วเขียนลงทั้งตาราง SQLite และ sorted set ของ Redis
  • ทุกคำขอ HTTP ที่เข้ามาต้องนำ IP ของคำขอไปเทียบกับช่วงที่ผู้ใช้กำหนดสำหรับอนุญาต/บล็อก ช่วง GeoIP และช่วงคะแนนความน่าเชื่อถือของ IP

โปรโตคอลการทดสอบ

  • ทดสอบบน M2 MacBook Air โดยใช้ Redis ที่ติดตั้งผ่าน Homebrew และ SQLite DB แบบโลคัล
  • ทดสอบกับชุดข้อมูลช่วงเดิมที่มี 1.2 ล้านรายการ
  • รันชุด IP หลายชุดกับ SQLite และ Redis ในลำดับเดียวกัน
  • ในแต่ละตัวคูณ รันทดสอบ 5 ครั้งแล้วหาค่าเฉลี่ย

ผลการทดสอบ

  • SQLite ชนะ Redis แบบขาดลอย (ใน use case เฉพาะของเรา)
  • SQLite เร็วกว่า Redis instance แบบโลคัลราว 3 เท่า
  • นี่ยังเป็นผลลัพธ์ก่อนพิจารณาความหน่วงเครือข่าย
  • ต่อให้ SQLite แค่เทียบชั้น Redis ได้ ก็ยังได้เปรียบเพราะตัดเวลาเครือข่ายออกไปได้

สิ่งที่ไม่มีในกราฟ

  • ต่อให้ประสิทธิภาพของ SQLite แย่ลง 2 เท่าใน benchmark ในโลกจริงก็อาจยังเร็วกว่าเพราะความหน่วงเครือข่าย
  • ไม่ว่า Redis server จะทรงพลังแค่ไหน ก็ยังมีข้อจำกัดเรื่องแบนด์วิดท์เครือข่าย การเชื่อมต่อ และความหน่วงข้ามภูมิภาค
  • SQLite สามารถสเกลแนวนอนได้แทบไม่จำกัดแบบ "ฟรี"
  • การ onboard ด้วย SQLite ดีขึ้นมาก ผู้ใช้อาจไม่ทันรู้ตัวด้วยซ้ำว่ามันกำลังถูกใช้งาน
  • แม้จะเค้นประสิทธิภาพจาก Redis ได้มากกว่านี้ แต่ก็ยากที่จะโน้มน้าวให้ผู้ใช้เปลี่ยนการตั้งค่า Redis

ผลลัพธ์เป็นเพียงจุดเริ่มต้น

  • แม้พิสูจน์ได้ว่า SQLite เร็วกว่า Redis แต่ก็มี trade-off จริงอยู่
  • การทดสอบข้างต้นไม่ได้คำนึงถึงการเขียนข้อมูล
  • การจัดการการแย่งกันระหว่างการอ่านและการเขียนยังต้องใช้สิ่งต่าง ๆ อย่างการเชื่อมต่อ connection pool และ transaction
  • เหมือนกับรถซูเปอร์คาร์ไฟฟ้าที่แบกบล็อกคอนกรีตได้ยาก จึงไม่ควรใช้ SQLite ในบทบาทที่ไม่เหมาะกับมัน

สร้างสถาปัตยกรรมการซิงก์

  • ใน v1 (Redis) เมื่อผู้ใช้อัปเดตกฎจาก Wafris Hub กฎในที่เก็บข้อมูล Redis ก็จะถูกอัปเดตตาม
  • แต่กับ SQLite วิธีนี้ใช้ไม่ได้ เพราะไม่สามารถ "push" ไปยังเว็บเซิร์ฟเวอร์ได้
  • ใน v2 (SQLite) กระบวนการคือ 1) ผู้ใช้อัปเดตกฎใน Wafris Hub 2) ไคลเอนต์ตรวจสอบกฎที่อัปเดตเป็นช่วง ๆ 3) หากมีกฎอัปเดต ก็จะดาวน์โหลด SQLite DB ลูกใหม่ทั้งก้อน
  • วิธีนี้ลดภาระการติดตั้งและคอนฟิกของผู้ใช้ลงอย่างมาก
  • อัตราความสำเร็จในการติดตั้งไคลเอนต์ v2 เพิ่มขึ้น 3 เท่า

สถาปัตยกรรมแบบกระจายด้วย SQLite

  • ลองนึกถึงแอป Rails ที่ดีพลอยบนผู้ให้บริการคลาวด์โดยเปิดใช้งาน auto-scaling
  • หากคำขอเพิ่มจาก 100req/s เป็น 10,000req/s instance ฝั่งคอมพิวต์จะสเกล แต่ DB จะไม่สเกลตาม
  • นี่คือสาเหตุหลักที่ทำให้แอป Rails ล่มจากภาวะโหลดเกินในโลกจริง
  • การซิงก์ SQLite DB ไปยังแต่ละ compute instance ช่วยแก้ปัญหานี้ได้ เพราะทำให้ทุกการเรียกเป็นแบบโลคัล

แล้วการเขียนล่ะ?

  • ทีมแยกแอปออกเป็นเส้นทางอ่าน (ประเมินกฎ) และเส้นทางเขียน (รายงานผล) แล้วเลือกมองข้ามเส้นทางเขียนไปก่อน
  • เส้นทางเขียนถูกออกแบบใหม่เป็น 1) เชื่อมต่อไปยัง Wafris Hub แบบอะซิงโครนัสเพื่อรายงาน 2) ส่งรายงานแบบแบตช์ 3) ตัดการเขียน DB ออกจากไคลเอนต์ทั้งหมด
  • วิธีนี้อาจไม่เหมาะกับคนอื่น แต่สิ่งที่เราใส่ใจคือผู้ใช้ที่ต้องการไคลเอนต์ Wafris ที่ติดตั้งง่ายและทำงานเร็ว

บทสรุป

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

7 ความคิดเห็น

 
aer0700 2024-09-26

แม้ว่า SQLite จะดีเพียงพออยู่แล้ว แต่ในกรณีนี้ก็อดคิดไม่ได้ว่า มันอาจจะเป็น use case ที่ไม่ค่อยเหมาะกับ Redis ตั้งแต่แรกหรือเปล่า...

 
kandk 2024-09-26

การทำเบนช์มาร์กบน M2 นี่ก็แบบว่า...

 
colossus 2024-09-29

งั้นต้องวัดกันทุก AWS instance เลยเหรอครับ? คุณคาดหวังจากโอเพนซอร์สมากเกินไปนะ

 
toru123 2024-09-27

ทำบนสภาพแวดล้อมเซิร์ฟเวอร์เดียวกัน แบบนี้มีปัญหาไหมครับ?
สำหรับเบนช์มาร์กจำเป็นต้องใช้ CPU รุ่นที่ระบุไว้โดยเฉพาะหรือเปล่าครับ...?

 
superwoou 2024-09-26

สิ่งที่ทำบน m2 อาจมีจุดไหนบ้างที่เป็นปัญหาได้? (นอกเหนือจากประเด็นที่ว่าสภาพแวดล้อมบริการจริงไม่ได้ใช้โปรเซสเซอร์ m2)

 
kandk 2024-09-26

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

 
[ความคิดเห็นนี้ถูกซ่อน]