27 คะแนน โดย GN⁺ 2025-12-03 | 2 ความคิดเห็น | แชร์ทาง WhatsApp
  • โครงสร้างผู้เขียนเดี่ยวและคุณสมบัติแบบ embedded ของ SQLite ได้รับการพิสูจน์ผ่านการทดลองว่าเป็นปัจจัยที่ช่วยเพิ่มทั้งความสามารถในการขยายตัวและประสิทธิภาพ
  • ภายใต้เงื่อนไขเดียวกัน Postgres ลดลงเหลือ 348 TPS เมื่อมีความหน่วงเครือข่าย ขณะที่ SQLite สามารถทำได้ 44,096 TPS ด้วยการตัดเครือข่ายออก
  • ด้วย การประมวลผลแบบ batch และธุรกรรมแบบละเอียดด้วย SAVEPOINT ที่อาศัยโมเดลผู้เขียนเดี่ยว ทำได้สูงสุด 186,157 TPS และในคอนฟิกที่เสถียรทำได้ 102,545 TPS
  • กฎของ Amdahl อธิบายคอขวดของฐานข้อมูลแบบเครือข่าย และ SQLite รักษาประสิทธิภาพสูงไว้ได้ด้วยการหลีกเลี่ยงข้อจำกัดนี้
  • ผลลัพธ์นี้เน้นย้ำทั้ง ความเป็นไปได้ของการใช้ SQLite ในสภาพแวดล้อมโลคัล และ ความสำคัญของการกำจัดคอขวดจากเครือข่าย

โครงสร้างของ SQLite และสภาพแวดล้อมการทดลอง

  • SQLite ไม่มี MVCC และอนุญาตให้มีผู้เขียนได้เพียงรายเดียว แต่โครงสร้างนี้กลับทำให้ขยายตัวได้ดีอย่างน่าประหลาด
    • ในฐานะฐานข้อมูลแบบ embedded จึงไม่มี network overhead
  • เบนช์มาร์กดำเนินการบน MacBook Pro (2021) พร้อม Apple M1 Pro และหน่วยความจำ 16GB
  • การทดลองนี้ไม่ได้มุ่งที่การจูนให้เหมาะสมที่สุดแบบสุดขั้ว แต่มีเป้าหมายเพื่อแสดงว่า แม้ในเงื่อนไขทั่วไปก็สามารถได้ throughput การเขียนที่สูงมาก

ความหมายของ TPS และตัวอย่างธุรกรรม

  • TPS ไม่ได้หมายถึงความเร็วในการเขียนอย่างเดียว แต่หมายถึง ธุรกรรมเชิงโต้ตอบ (Interactive Transaction)
    • ตัวอย่าง: การโอนเงินระหว่างบัญชี ซึ่งมีหลาย query และโค้ดแอปพลิเคชันทำงานอยู่ภายในธุรกรรมเดียวกัน
  • ธุรกรรมสามารถ rollback สถานะได้เมื่อเกิดข้อผิดพลาด จึงมี บทบาทสำคัญต่อการรักษาความสอดคล้องของข้อมูล

การตั้งค่าเบนช์มาร์ก

  • ใช้ virtual threads บน Clojure เพื่อจำลองคำขอพร้อมกันจำนวนมาก
  • Postgres ตั้งค่าด้วย connection pool บน HikariCP ส่วน SQLite ใช้ ผู้เขียนเดี่ยวและการเชื่อมต่อสำหรับการอ่านตามจำนวนคอร์
  • ทั้งสองฐานข้อมูลใช้ตาราง account แบบง่ายที่มีฟิลด์ id, balance และแทรกข้อมูล 1 พันล้านแถว
  • กิจกรรมของผู้ใช้เป็นไปตาม การกระจายแบบ power law (0.9995) และมี ผู้ใช้ที่แอ็กทีฟประมาณ 100,000 คน

ประสิทธิภาพของฐานข้อมูลแบบเครือข่าย (Postgres)

  • ภายในเซิร์ฟเวอร์เดียวกัน Postgres ทำได้ 13,756 TPS
  • เมื่อเพิ่มความหน่วงเครือข่าย 5ms ลดลงเหลือ 1,214 TPS และที่ 10ms ลดลงฮวบเป็น 702 TPS
  • หลังใช้ระดับการแยกธุรกรรมแบบ serializable ลดลงเป็น 660 TPS และเมื่อรวม query เพิ่มเติมลดลงเหลือ 348 TPS
  • สิ่งนี้แสดงให้เห็นตาม กฎของ Amdahl ว่าคอขวดจากเครือข่ายเป็นตัวจำกัดประสิทธิภาพโดยรวม
    • เมื่อความหน่วงเครือข่ายเพิ่มขึ้น การแย่ง lock ระหว่างธุรกรรมจะรุนแรงขึ้นจนไม่สามารถขยายตัวได้

ข้อได้เปรียบของ SQLite แบบ embedded

  • หลังตัดเครือข่ายออก SQLite ทำได้ 44,096 TPS
    • เมื่อคอขวดจากเครือข่ายหายไป ผลกระทบจากกฎของ Amdahl ก็ลดลงอย่างมาก
  • เมื่อใช้ประโยชน์จากโครงสร้างผู้เขียนเดี่ยวและนำ batch processing มาใช้ throughput เพิ่มขึ้นถึง 186,157 TPS
    • มีการปรับขนาด batch แบบไดนามิกเพื่อเพิ่มประสิทธิภาพ latency และ throughput โดยอัตโนมัติ

ธุรกรรมแบบละเอียดด้วย SAVEPOINT

  • เพื่อป้องกันไม่ให้ความล้มเหลวของธุรกรรมรายตัวใน batch กระทบทั้งชุด จึงใช้ nested transaction ด้วย SAVEPOINT
    • หากล้มเหลวจะ rollback เฉพาะธุรกรรมนั้น และคง batch ทั้งหมดไว้
  • แม้ใช้วิธีนี้ก็ยังรักษาระดับได้ที่ 121,922 TPS

การทดสอบโหลดแบบผสมอ่าน/เขียน

  • กำหนดให้ 75% ของคำขอทั้งหมดเป็นการอ่าน และ 25% เป็นการเขียน
  • ใช้ thread pool สำหรับการอ่านแยกต่างหากเพื่อ แยกไม่ให้คำขออ่านไปรบกวนการเขียน
  • ผลลัพธ์สุดท้ายทำได้ 102,545 TPS

สรุปการเปรียบเทียบประสิทธิภาพ

เงื่อนไข Postgres SQLite
ไม่มีเครือข่าย 13,756 44,096
หน่วง 5ms 1,214 n/a
หน่วง 10ms 702 n/a
10ms + serializable 660 n/a
batch processing n/a 186,157
batch + SAVEPOINT n/a 121,922
batch + SAVEPOINT + การอ่าน n/a 102,545

บทสรุป

  • SQLite สามารถทำ TPS ได้สูงกว่าฐานข้อมูลแบบเครือข่ายอย่างมาก ด้วย โมเดลผู้เขียนเดี่ยวและโครงสร้างแบบ embedded
  • การหลีกเลี่ยง ข้อจำกัดคอขวดจากเครือข่ายตามที่กฎของ Amdahl ชี้ให้เห็น ทำให้รีดประสิทธิภาพได้อย่างเต็มที่
  • โค้ดทั้งหมดเปิดเผยไว้บน GitHub และยังมีเอกสารประกอบในหัวข้อที่เกี่ยวข้อง เช่น กฎของ Amdahl, power law และกรณีการขยายระบบด้วย SQLite
  • SQLite เป็นตัวเลือกที่มีประสิทธิภาพมากสำหรับ การประมวลผลธุรกรรมประสิทธิภาพสูงในสภาพแวดล้อมโลคัล

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

 
ppp123 2025-12-10

ถ้าใช้แค่ในเครื่องภายในสภาพแวดล้อมโลคัลโดยไม่ต้องไปพึ่งเซิร์ฟเวอร์ภายนอก ก็เท่ากับว่าถามว่า จำเป็นต้องเสียภาษีเครือข่ายด้วยเหรอ? (VFS vs Socket)

 
GN⁺ 2025-12-03
ความคิดเห็นจาก Hacker News
  • ฉันกำลังสร้าง เซิร์ฟเวอร์ ORM/CRUD แบบไฮบริด protobuf ที่ใช้ SQLite
    โค้ดและคำอธิบายอยู่ที่ GitHub - accretional/collector
    ระหว่างแบ็กอัปแบบเรียลไทม์มี downtime 5~15ms, คิวคำขออ่าน/เขียนได้หลายร้อยรายการ, latency ของ CRUD ทั้งหมดอยู่ราว 1ms, และยังทำ streaming backup บน WAL ได้อีกด้วย
    เมื่อก่อนฉันใช้แต่ Postgres กับ Spanner แต่ถ้าเพิ่มฟีเจอร์พาร์ทิชันให้ Collector ได้ ฉันคงไม่กลับไปใช้ Postgres อีก

    • สงสัยว่าเคยพิจารณาวิธีทำ แบ็กอัปแบบไม่มีดาวน์ไทม์ ด้วย SQLite + WAL บนไฟล์ซิสเต็มที่มี atomic snapshot อย่าง BTRFS ไหม หลังสแนปช็อตแล้วก็ค่อย ๆ แบ็กอัปและลบทิ้งทีหลังได้
  • ข้อเสียคือข้อมูลและการประมวลผลทั้งหมดต้องอยู่ใน เครื่องเดียว
    ถ้าใช้ AWS instance รุ่น u-24tb1.112xlarge (448 vcore, RAM 24TB, EBS 64TB) ก็ยังเหลือเฟือพอสมควร

    • แต่ถ้าเช่า เซิร์ฟเวอร์ bare metal ของ Hetzner จะได้ประสิทธิภาพต่อคอร์สูงกว่า 2~3 เท่า และลดค่าใช้จ่ายได้ 90%
    • ในเอกสารทางการระบุว่า ขนาด DB สูงสุดตามทฤษฎี ของ SQLite คือ 281TB ในทางปฏิบัติข้อจำกัดของไฟล์ซิสเต็มจะต่ำกว่านั้น แต่ก็ยังทำงานได้ปกติ
    • การขยายแบบ scale-up บนเครื่องเดียวมีความเสถียร แต่ขาด elasticity ต้องเผื่อทรัพยากรเกินไว้ตอนทราฟฟิกพุ่ง หรือไม่ก็ยอมรับความขัดข้อง
    • ดูลิงก์ yourdatafitsinram.net ที่ถามว่า “ข้อมูลของคุณใส่ใน RAM ได้ไหม?” แล้ว ฉันคิดว่าถ้าเป็น single node ประสิทธิภาพสูง เซิร์ฟเวอร์เฉพาะน่าจะดีกว่า EC2
  • บทความเน้นประสิทธิภาพของ SQLite ก็จริง แต่รู้สึกว่า เกณฑ์เปรียบเทียบไม่ชัดเจน
    เพราะเดิมทีตั้งสมมติฐานเป็นสถาปัตยกรรมเซิร์ฟเวอร์แยกกัน แล้วมาวัดประสิทธิภาพของ local embedded DB
    ถ้าเงื่อนไขเดียวกัน การจูน Postgres แบบรันในเครื่องเดียวกันก็น่าจะให้ประสิทธิภาพใกล้เคียงได้

    • SQLite เร็วกว่า Postgres ที่อยู่บนเครื่องเดียวกันด้วยซ้ำ การทดสอบโดยอิงตามสภาพแวดล้อม deployment จริงถือว่าสมเหตุสมผล
    • คุณสามารถ ครอบ SQLite ไว้หลัง request handler แล้วให้ไปรันบนเซิร์ฟเวอร์อีกเครื่องก็ได้ สุดท้ายแล้ว DB ก็เป็นแค่การรวมกันของตัวประมวลผลคำขอกับที่เก็บข้อมูล
    • บนเครื่องเดียว raw throughput เป็นเรื่องสำคัญ SQLite เร็วกว่า PG 10 เท่า และ PG จะยิ่งช้าลงเมื่อทรานแซกชันซับซ้อนขึ้น
    • การบอกว่า “งั้น SQLite ก็ไม่ใช่ตัวเปรียบเทียบ” มันง่ายเกินไป บทความคงสั้นเกิน
    • SQLite ไม่ได้เหมาะแค่กับมือถือหรืออุปกรณ์ฝังตัว แต่ยังเหมาะกับ แอปเซิร์ฟเวอร์ที่มี concurrency ไม่สูง ด้วย มันไม่ใช่ DB สำหรับเว็บเซิร์ฟเวอร์เท่านั้น
  • การจำกัดจำนวนการเชื่อมต่อ Postgres ไว้ที่ 8 อาจเป็น คอขวด
    น่าจะเผยการใช้ CPU และเธรดควบคู่กันไป และลองทดสอบใหม่ด้วย connection pool ที่ใหญ่ขึ้น

    • การตั้ง connection pool ตามจำนวนคอร์ (8) ก็โอเค แต่ถ้ามี sleep อยู่ในทรานแซกชันจะเกิดคอขวดได้
      ถ้าเพิ่มเป็น 64 connections throughput อาจเพิ่มได้ 8 เท่า ควร ขยายการตั้งค่าฝั่งไคลเอนต์ ไปเรื่อย ๆ จนกว่าจะชนขีดจำกัด
    • ตัวเลขในบทความนี้เชื่อยาก ฉันยังทำ TPS ได้สูงกว่านี้มากแม้บน MySQL แบบมีเครือข่าย
  • ประเด็นสำคัญคือ ต้องตระหนักว่า network latency เป็นคอขวดหรือไม่
    ในหลาย workload นั้น local DB ธรรมดา ๆ ก็เร็วกว่าระบบ remote DB ที่ยอดเยี่ยม
    สิ่งสำคัญไม่ใช่ “DB ตัวไหนดีที่สุด” แต่คือ “จำเป็นต้องข้ามขอบเขตเครือข่ายหรือเปล่า

    • (ผู้เขียน) ใช่แล้ว ผมไม่ได้ตั้งใจให้เป็นการถก SQLite vs Postgres แต่ต้องการพูดถึง ข้อจำกัดของ DB แบบเครือข่าย
    • แน่นอนว่าถ้าเก็บทุกอย่างไว้ในหน่วยความจำแล้วใช้ Redis หรือ Memcache ประสิทธิภาพก็พุ่งได้ง่าย แต่แบบนั้นถือว่าเปลี่ยนกติกาไปแล้ว
  • DB แบบเครือข่าย มีข้อดีตรง redeploy แอปได้ง่าย
    เปิดอินสแตนซ์ใหม่แล้วปิดตัวเดิม ก็ทำ การ deploy แบบแทบไม่มี downtime ได้
    แต่ถ้า SQLite อยู่ในอินสแตนซ์เดียวกัน พอจะสลับเครื่องก็ต้องย้าย DB ขึ้นมาใหม่ ทำให้ซับซ้อนกว่า อยากรู้ว่าเคยเจอปัญหานี้ในการใช้งานจริงไหม

    • ถ้าจะใช้ SQLite ในโปรดักชัน ต้องมี persistent storage และ NVMe ปกติมักรันบนเซิร์ฟเวอร์เดี่ยวแบบ bare metal
      ตอนย้ายระบบอาจมี downtime ได้ แต่ตอนนี้ Litestream ทำให้การทำ replication และ backup ง่ายขึ้นมาก
    • SQLite รองรับ การเข้าถึงแบบหลายโปรเซส ดังนั้นจึงทำการสลับแบบไม่สะดุดได้ด้วยวิธีเปิดโปรเซสใหม่แล้วปิดตัวเก่า
  • ผู้เขียนตั้งค่า PRAGMA synchronous="normal" ซึ่งหมายความว่า ไม่ได้ทำ fsync ทุกครั้ง
    เพื่อความยุติธรรมในการเปรียบเทียบ ควรตั้งเป็น "full"

    • แต่ในโหมด WAL ค่า "normal" ก็ใช้ได้ การสูญเสียไฟจะทำให้เสียความทนทาน แต่ยังคง ความสอดคล้องของทรานแซกชัน เอาไว้ได้
  • อยากรู้ว่าการทำ HA (ความพร้อมใช้งานสูง) ของ SQLite ทำอย่างไร
    อย่างน้อยควรอยู่ในระดับที่ทำ failover อัตโนมัติได้

    • SQLite เป็นไลบรารีภาษา C จึงขยายต่อได้ด้วยโปรเจกต์อย่าง rqlite, litestream, litefs
      ตอนนี้ฉันกำลังชั่งใจระหว่าง Postgres กับ SQLite (รวม litestream)
      แอปของฉันยอมรับ downtime ได้เล็กน้อย ดังนั้นการขยายแนวตั้งบนเครื่องเดียวจึงง่ายและถูกกว่า
    • ช่วงหลังโปรเจกต์มัลติมาสเตอร์ชื่อ Marmot กลับมามีชีวิตอีกครั้งหลังจากเงียบไป 2 ปี
      ที่ Marmot GitHub มีการเพิ่ม กลไก replication แบบ gossip-based เข้ามาใหม่
  • อยากรู้ว่ามีกรณีไหนบ้างที่ใช้ SQLite ใน โปรดักชันจนดันไปถึงขีดจำกัดจริง ๆ

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