
DuckDB หรือ SQLite: งานวิเคราะห์ล้านแถวกับธุรกรรมเล็กต้องเลือกคนละแบบ

ถ้างานหลักคือสแกนข้อมูลจำนวนมากเพื่อรวมยอด จัดกลุ่ม หรือทำรายงาน ให้เริ่มพิจารณา DuckDB; ถ้างานหลักคือเพิ่ม แก้ไข และค้นหารายการเฉพาะในแอป ให้เริ่มจาก SQLite บททดสอบข้อมูลอีคอมเมิร์ซสังเคราะห์หนึ่งล้านแถว ใช้คิวรีวิเคราะห์สิบแบบ และรายงานว่าคิวรีจัดกลุ่มตามหมวดสินค้าใช้เวลา 0.020 วินาทีใน DuckDB เทียบกับ 2.275 วินาทีใน SQLite ภายใต้สภาพแวดล้อมของผู้ทดสอบ ตัวเลขนี้บอกความต่างของงานสรุปข้อมูลในชุดทดสอบนั้น ไม่ใช่ความเร็วของทุกคำสั่งในแอป
จำนวนแถวจึงเป็นเพียงส่วนหนึ่งของคำตอบ ตารางขนาดใหญ่ที่แอปเปิดด้วยรหัสและแก้ไขทีละรายการยังมีลักษณะเป็นงานธุรกรรม ขณะที่ตารางเล็กกว่าซึ่งต้องสแกนซ้ำเพื่อคำนวณหลายมิติอาจเป็นงานวิเคราะห์ คำถามที่ใช้เลือกได้ตรงกว่าคือแต่ละคำขอต้องแตะข้อมูลมากเพียงใด เขียนบ่อยแค่ไหน และมีโปรเซสใดต้องเปิดไฟล์เดียวกันเพื่อเขียนพร้อมกันบ้าง
ทำไมการอ่านทั้งตารางกับการแก้ไขหนึ่งรายการจึงต่างกัน
DuckDB จัดข้อมูลสำหรับงานวิเคราะห์แบบคอลัมน์และประมวลผลเป็นชุด หากต้องหายอดขายจากราคาและจำนวนสินค้า ระบบสามารถอ่านค่าที่คิวรีต้องใช้จากรายการจำนวนมากแล้วส่งต่อให้ขั้นรวมผลได้อย่างต่อเนื่อง การเก็บค่าชนิดเดียวกันไว้ใกล้กันยังเอื้อต่อการบีบอัดและลดงานอ่านคอลัมน์ที่ไม่เกี่ยวข้อง ประโยชน์นี้เด่นเมื่อคำตอบสุดท้ายมีเพียงยอดรวมหรือกลุ่มเล็ก ๆ แต่ต้องพิจารณาระเบียนวงกว้าง
SQLite เก็บระเบียนในโครงสร้าง B-tree แบบเน้นแถวและใช้ดัชนีเพื่อนำทางไปยังข้อมูลที่ต้องการ หากแอปทราบรหัสคำสั่งซื้อ การค้นหาและแก้ไขระเบียนนั้นเป็นงานคนละลักษณะกับการอ่านคำสั่งซื้อทั้งช่วงเวลาเพื่อรวมยอด การเก็บข้อมูลของหนึ่งระเบียนไว้ด้วยกันช่วยงานที่ต้องอ่านหลายช่องของรายการเดียว ขณะที่การสแกนเพื่อคำนวณค่าจากหลายรายการต้องผ่านระเบียนที่เกี่ยวข้องจำนวนมาก
ความต่างนี้ไม่ได้แปลว่า SQLite ทำรายงานไม่ได้ ดัชนีที่ตรงกับตัวกรองอาจลดจำนวนแถวที่ต้องอ่าน และดัชนีที่ครอบคลุมคอลัมน์ในคิวรีอาจลดการย้อนกลับไปอ่านตารางจริงได้ด้วย แต่ดัชนีมีต้นทุนพื้นที่และต้องได้รับการปรับปรุงเมื่อข้อมูลเปลี่ยน หากรายงานต้องรวมข้อมูลเกือบทั้งตาราง ดัชนีที่ดีสำหรับการค้นหารายการเฉพาะก็อาจช่วยได้ไม่มาก การเปรียบเทียบจึงควรดูรูปแบบคิวรีพร้อมแผนการทำงานของฐานข้อมูล
ลองนึกถึงแอปคำสั่งซื้อในสถานการณ์สมมติ หน้ารายละเอียดรับรหัสแล้วแสดงสินค้ากับสถานะของคำสั่งซื้อหนึ่งรายการ ส่วนรายงานรายเดือนต้องอ่านราคา จำนวน และหมวดสินค้าจากคำสั่งซื้อจำนวนมาก ทั้งคู่ใช้ข้อมูลชุดเดียวกัน แต่ปริมาณข้อมูลที่ต้องผ่านเครื่องประมวลผลต่อคำตอบต่างกันมาก การเลือกจากขนาดไฟล์อย่างเดียวจึงอาจทำให้แก้ปัญหารายงานช้าโดยไปกระทบเส้นทางบันทึกข้อมูลที่ใช้งานตลอดวัน
ผลทดสอบวัดภาระงานใด และบอกอะไรได้จริง
งานวิจัยที่ตีพิมพ์ใน PVLDB ปี 2022 ทดสอบงานธุรกรรมขนาดเล็กด้วย TATP และงานวิเคราะห์ด้วย Star Schema Benchmark หรือ SSB โดยพบว่า SQLite ในโหมด WAL ให้ปริมาณธุรกรรมสูงสุดในการตั้งค่าที่ศึกษา ขณะที่ DuckDB ตอบคิวรี SSB ทุกชุดได้เร็วกว่า SQLite งาน TATP จำลองคำขอเบา ๆ ของระบบโทรคมนาคม ส่วน SSB ใช้การเชื่อมตารางและตัวกรองในลักษณะคลังข้อมูล ผลทั้งสองฝั่งจึงวัดคนละคำถาม ไม่ควรนำตัวเลขเวลารายงานไปใช้แทนเวลาบันทึกหนึ่งรายการ
การทดลองดังกล่าวใช้ซอฟต์แวร์รุ่นและฮาร์ดแวร์ของช่วงที่ศึกษา รวมถึงกำหนดจำนวนเธรดของ DuckDB และจำลองผู้ใช้ธุรกรรมตามวิธีวิจัย ผลที่ได้จึงเป็นหลักฐานว่าลักษณะงานมีผลต่อความได้เปรียบ มากกว่าจะเป็นอัตราเร็วที่แอปทุกรุ่นจะทำได้ หากระบบจริงมีคิวรีที่เลือกข้อมูลได้แคบมาก ใช้ดัชนีต่างกัน หรือมีผู้ใช้เขียนพร้อมกัน ผลอาจเปลี่ยนตามเงื่อนไขเหล่านั้น
บททดสอบอีคอมเมิร์ซในช่วงต้นให้ภาพของคิวรีรวมยอดบนข้อมูลสังเคราะห์ แต่มีรายละเอียดวิธีวัดที่ต้องอ่านให้ตรงกัน คำอธิบายระบุว่า DuckDB อ่าน CSV โดยตรง ขณะที่ SQL ที่เผยแพร่สร้างตารางจาก CSV ก่อนรันคิวรี และฝั่ง SQLite ก็นำเข้า CSV ก่อนเช่นกัน เวลาคิวรีที่รายงานจึงไม่ควรถูกใช้แทนเวลาตั้งแต่รับไฟล์ดิบจนได้ผลลัพธ์ หากงานจริงต้องโหลดไฟล์ใหม่ทุกครั้ง เวลานำเข้าจะเป็นส่วนหนึ่งของต้นทุนที่ต้องนับแยก
การอ่านผลทดสอบยังต้องดูว่าตารางมีดัชนีอะไร เปิดข้อมูลไว้ในหน่วยความจำแล้วหรือยัง และวัดการรันครั้งแรกหรือครั้งถัดไป คิวรีค้นหารหัสอาจได้ประโยชน์จากดัชนีอย่างมาก ส่วนคิวรีสรุปยอดทั้งตารางอาจใช้เส้นทางอ่านต่างออกไป การจับเวลาเฉพาะ SQL โดยไม่รวมการเปิดไฟล์และเตรียมข้อมูลเหมาะกับรายงานที่รันซ้ำบนข้อมูลเดิมมากกว่างานสำรวจไฟล์ใหม่เป็นครั้งคราว
ผล benchmark ทางการมีขอบเขตของมัน
คำอธิบาย benchmark ของ DuckDB ระบุว่าใช้ข้อมูลและคิวรีบางส่วนจาก TPC-H กับ LDBC BI เพื่อประกอบคำแนะนำด้านประสิทธิภาพ แต่ผลที่เผยแพร่ไม่ใช่ผล TPC หรือ LDBC อย่างเป็นทางการ และไม่ได้รวมภาระงานทุกชนิด เช่น การอัปเดตข้อมูล การเห็นชื่อชุดทดสอบเดียวกันจึงยังไม่พอให้สรุปว่าการทดสอบสองแห่งใช้วิธีวัดเท่ากัน
โดยเฉพาะงานผสม ตัวเลขการสแกนและรวมข้อมูลไม่บอกเวลารอของคำสั่งเขียน ความถี่ในการคอมมิต หรือผลของการเปิดไฟล์จากหลายโปรเซส ในทางกลับกัน ผลธุรกรรมสั้นก็ไม่อธิบายต้นทุนรายงานที่ต้องเชื่อมตารางและจัดกลุ่มข้อมูลวงกว้าง การวางผลทั้งสองประเภทไว้คู่กันช่วยระบุว่าฐานข้อมูลแต่ละตัวแก้ภาระงานส่วนใดของระบบ มากกว่าตัดสินจากตารางอันดับความเร็วตารางเดียว
จำนวนผู้เขียนเปลี่ยนคำตอบเรื่องฐานข้อมูลหลัก
เอกสาร WAL ของ SQLite อธิบายว่าผู้อ่านทำงานระหว่างมีการเขียนได้ แต่มีผู้เขียนได้ทีละราย และโปรเซสที่ใช้ไฟล์ในโหมดนี้ต้องอยู่บนเครื่องเดียวกันเพราะอาศัยหน่วยความจำร่วม ดังนั้นหลายการเชื่อมต่อไม่ได้หมายถึงหลายธุรกรรมเขียนที่ทำพร้อมกันได้โดยไม่มีเวลารอ หากแอปถือธุรกรรมไว้นาน คำขอเขียนอื่นย่อมต้องรอคิว แม้คิวรีอ่านจะยังทำงานได้
WAL ยังมีขั้นนำข้อมูลจากบันทึกกลับสู่ไฟล์ฐานข้อมูล หากมีการอ่านค้างนาน ขั้นนี้อาจเดินหน้าได้ไม่เต็มที่ จึงควรนับพฤติกรรมการอ่านยาว ๆ และอายุของธุรกรรมเมื่อออกแบบแอปด้วย สำหรับงานแก้ไขสถานะสั้นและบ่อย การทำธุรกรรมให้สั้นช่วยลดเวลาที่คำขอเขียนอื่นต้องรอ ส่วนการรวบการเปลี่ยนแปลงหลายรายการในธุรกรรมเดียวมีผลต่อต้นทุนคอมมิตและควรทดสอบตามรูปแบบที่ใช้งานจริง
เอกสารการเข้าถึงพร้อมกันของ DuckDB ระบุว่าในโหมดฝังตัวซึ่งเปิดไฟล์แบบอ่านเขียน มีหนึ่งโปรเซสที่อ่านและเขียนได้ หลายเธรดภายในโปรเซสนั้นเขียนได้หากไม่แก้ข้อมูลชนกัน ส่วนการเปิดอ่านจากหลายโปรเซสใช้โหมดอ่านอย่างเดียวเมื่อไม่มีผู้เขียน ข้อจำกัดนี้เกี่ยวกับวิธีเปิดไฟล์แบบฝังตัวโดยตรง หากแอปมีหลายโปรเซสที่ต่างต้องบันทึกรายการลงไฟล์เดียว การออกแบบทางเข้าของงานเขียนย่อมสำคัญพอ ๆ กับความเร็วของคิวรีวิเคราะห์
DuckDB มีธุรกรรมและจัดการการเขียนพร้อมกันภายในโปรเซสได้ จึงไม่ควรตีความว่าใช้เขียนข้อมูลไม่ได้ ประเด็นคือการเพิ่มข้อมูลเป็นชุดจากกระบวนการที่ควบคุมได้ กับการให้โปรเซสของแอปหลายตัวคอมมิตการแก้ไขรายการเล็กอย่างต่อเนื่อง เป็นภาระงานต่างกัน หากระบบต้องทำทั้งสองอย่าง การคงเส้นทางรับธุรกรรมไว้ใน SQLite แล้วส่งข้อมูลไปยังชั้นวิเคราะห์เป็นทางเลือกเชิงสถาปัตยกรรม แต่ต้องยอมรับต้นทุนการส่งข้อมูลและความล่าช้าของรายงาน
แผนผังเลือกจากชนิดคิวรีและวิธีเขียน
ให้เริ่มจากงานที่ผู้ใช้รอคำตอบจริง หากคำขอส่วนใหญ่ค้นหาด้วยคีย์ เพิ่มรายการ หรือเปลี่ยนสถานะ และหลายส่วนของแอปต้องเข้าถึงฐานข้อมูลเดียวกัน SQLite เป็นจุดตั้งต้นที่ตรงลักษณะงานกว่า หากคำถามหลักคือยอดรวมตามช่วงเวลา การจัดกลุ่มหลายมิติ หรือการเชื่อมข้อมูลจำนวนมากจากการนำเข้าเป็นรอบ DuckDB เป็นตัวเลือกที่ตรงกับรูปแบบการอ่านมากกว่า ขนาดข้อมูลช่วยประเมินต้นทุนสแกน แต่ไม่ใช่เส้นแบ่งตายตัวระหว่างสองระบบ
- อ่านเฉพาะรายการและเขียนถี่: วาง SQLite เป็นฐานข้อมูลหลัก แล้วออกแบบดัชนีจากเงื่อนไขค้นหาที่เกิดจริง ตรวจด้วยว่าธุรกรรมเขียนใช้เวลานานเพียงใด เพราะ WAL ยังมีผู้เขียนได้ทีละราย
- อ่านข้อมูลวงกว้างและเขียนเป็นชุด: พิจารณา DuckDB เมื่อคิวรีต้องรวมค่าจากหลายแถวซ้ำ ๆ โดยเฉพาะงานสำรวจไฟล์หรือสร้างรายงานที่ควบคุมกระบวนการอ่านเขียนได้
- มีทั้งธุรกรรมและรายงานหนัก: แยกหน้าที่รับการเขียนออกจากหน้าที่วิเคราะห์ได้ แต่ต้องกำหนดว่ารายงานยอมรับข้อมูลล่าช้าเท่าไร และจะนับเวลาคัดลอกหรือนำเข้าข้อมูลอย่างไร
ไม่มีสัดส่วนการอ่านต่อการเขียนตัวเลขเดียวที่ใช้ได้กับทุกแอป รายงานที่รันนานแต่เรียกเพียงเป็นครั้งคราวอาจยังไม่คุ้มกับการดูแลข้อมูลอีกชุด ขณะที่รายงานที่ต้องตอบบ่อยและสแกนข้อมูลเกือบทั้งตารางอาจแย่งทรัพยากรจากงานบันทึกประจำวัน ควรดูทั้งความถี่และเวลาที่แต่ละงานกินไป รวมถึงผลเมื่อรายงานกับงานเขียนเกิดพร้อมกัน
ต้นทุนของการใช้สองระบบไม่ได้มีเพียงพื้นที่เก็บ ต้องกำหนดด้วยว่าข้อมูลใดเป็นฉบับหลัก จะส่งการแก้ไขและการลบตามไปอย่างไร และรายงานจะอ่านข้อมูลถึงจุดเวลาใด หากผู้ใช้คาดหวังยอดล่าสุดทันที ความล่าช้าระหว่างฐานข้อมูลธุรกรรมกับชั้นวิเคราะห์อาจสำคัญกว่าการลดเวลาคิวรีลงมาก การแยกบทบาทจึงเหมาะเมื่อความสดของข้อมูลที่ยอมรับได้ชัดเจน
ชุดคิวรีที่ทำให้การเลือกตรงกับงานจริง
ตัวอย่างต่อไปนี้เป็นสถานการณ์สมมติ ไม่ใช่ผลทดสอบใหม่ สมมติว่าทั้งสองระบบมีตาราง orders ซึ่งประกอบด้วย order_id, category, amount และ created_at และได้รับข้อมูลชุดเดียวกัน คิวรีที่ใช้จับเวลาควรแทนเส้นทางสำคัญของแอป ทั้งการค้นหารายการ การสรุปข้อมูล และการแก้ไขรายการ พร้อมตรวจว่าผลลัพธ์ตรงกันก่อนเปรียบเทียบเวลา
- ค้นหารายการเฉพาะ: SELECT order_id, category, amount FROM orders WHERE order_id = ?; งานนี้ตอบว่าการเปิดระเบียนตามรหัสเร็วเพียงใด ให้ใช้ดัชนีแบบเดียวกับที่ตั้งใจใช้จริง และดูแผนคิวรีว่าระบบเข้าถึงระเบียนผ่านดัชนีหรือสแกนตาราง
- สรุปข้อมูลวงกว้าง: SELECT category, SUM(amount) AS total_amount FROM orders GROUP BY category; งานนี้อ่านค่าจากหลายรายการเพื่อให้ได้ยอดต่อหมวด หากระบบจริงมีตัวกรองวันที่หรือการเชื่อมตาราง ให้เพิ่มเงื่อนไขเหล่านั้นในคิวรีทดสอบด้วย
- แก้ไขรายการ: UPDATE orders SET amount = ? WHERE order_id = ?; วัดตั้งแต่เริ่มธุรกรรมจนคอมมิต และทดสอบรูปแบบคอมมิตตามที่แอปทำจริง การจับเวลาเฉพาะคำสั่ง UPDATE จะมองข้ามต้นทุนที่ผู้ใช้ต้องรอ
แยกเวลานำเข้าข้อมูล เวลาเปิดฐานข้อมูล และเวลารันคิวรีออกจากกัน การรันครั้งแรกอาจมีต้นทุนต่างจากการรันซ้ำเมื่อข้อมูลอยู่ในแคชแล้ว จึงควรเก็บผลทั้งสองแบบแทนการเลือกเฉพาะค่าที่ดูดีที่สุด ตรวจชนิดข้อมูล จำนวนแถวที่คืน และดัชนีให้เทียบกันได้ด้วย มิฉะนั้นฐานข้อมูลอาจตอบคำถามคนละข้อโดยที่เวลาแสดงผลดูเหมือนเปรียบเทียบกันได้
หากแอปมีหลายโปรเซส ให้จำลองจำนวนผู้เขียนและผู้อ่านตามการใช้งานจริงด้วย คิวรีเดี่ยวที่รันเงียบ ๆ ไม่แสดงเวลารอเขียน ความขัดแย้งของการแก้ไข หรือผลของรายงานที่อ่านนาน เมื่อผลจากคิวรีค้นหา คิวรีสรุป และธุรกรรมถูกวางคู่กับความถี่ใช้งาน จะเห็นว่าควรให้ระบบใดรับงานหลัก และรายงานจำเป็นต้องแยกชั้นวิเคราะห์หรือไม่
บทความที่เกี่ยวข้อง


Supabase หรือ Firebase: งานอ่านกับงานเขียนให้คำตอบคนละแบบ

Google Data Agent Kit เปิดใช้จริง แต่คำสั่งเดียวแตะข้อมูลสดได้

UiPath Delegate ทำงานข้ามแอปได้แล้ว แต่สิทธิ์ผู้ใช้ยังเป็นเพดาน

BigQuery หรือ Snowflake: ข้อมูลโตถึงจุดหนึ่ง ผู้ชนะด้านต้นทุนอาจสลับข้าง

World Space Week 2026 เริ่มแล้ว—จรวดใช้ซ้ำคือหัวใจของปีนี้
สมัครรับจดหมายข่าวของเรา
รับข่าวสารล่าสุดเกี่ยวกับ Web3, AI และคริปโตส่งตรงถึงกล่องจดหมายของคุณ