เปรียบเทียบ DuckDB กับ SQLite เลือกใช้อย่างไรบนเซิร์ฟเวอร์
ไขข้อสงสัยการเลือกใช้ DuckDB และ SQLite บน VPS เดียวกัน วิเคราะห์ความแตกต่างระหว่าง OLTP และ OLAP พร้อมตัวอย่างการใช้งานจริงเพื่อจัดการข้อมูล Parquet และ CSV อย่างมีประสิทธิภาพ
การเปรียบเทียบ DuckDB กับ SQLite บนเซิร์ฟเวอร์: คำตอบในประโยคเดียว
SQLite เป็นเอนจินแบบ OLTP (online transaction processing) ซึ่งจัดเก็บข้อมูลในรูปแบบแถวและถูกสร้างมาเพื่ออ่านหรือเขียนข้อมูลทีละจำนวนน้อยได้อย่างรวดเร็วและปลอดภัย ส่วน DuckDB เป็นเอนจินแบบ OLAP (online analytical processing) ซึ่งจัดเก็บข้อมูลในรูปแบบคอลัมน์และถูกสร้างมาเพื่อสแกนข้อมูลหลายล้านแถวเพื่อสรุปผล ทั้งสองเป็นไลบรารีแบบฝังตัว (embedded library) ที่เปิดไฟล์ข้อมูลโดยตรงและไม่ต้องรันโปรเซสเซิร์ฟเวอร์แยกต่างหากให้คุณต้องคอยดูแล
ดังนั้นคำตอบที่ตรงไปตรงมาสำหรับคำถามที่ว่า "ควรเลือกตัวไหน" มักจะเป็น "ใช้ทั้งคู่บน VPS เดียวกัน" โดยให้แอปพลิเคชันของคุณเก็บสถานะการทำงานจริงไว้ใน SQLite และใช้ DuckDB สำหรับการอ่านไฟล์ Parquet หรือ CSV เพื่อทำรายงาน ทั้งสองเครื่องมือนี้ไม่ได้แข่งขันกันเพราะทำหน้าที่ต่างกันอย่างชัดเจน
เหตุผลที่การจัดเก็บแบบแถวและแบบคอลัมน์ส่งผลต่อคำตอบ
SQLite เขียนข้อมูลหนึ่งแถวให้เป็นส่วนที่ต่อเนื่องกันภายในหน้าข้อมูล (page) การดึงข้อมูลคำสั่งซื้อหนึ่งรายการโดยใช้ primary key จะเข้าถึงหน้าดัชนีหนึ่งหน้าและหน้าข้อมูลหนึ่งหน้า ซึ่งเท่ากับการอ่าน 2 ครั้ง นี่คือสิ่งที่แอปพลิเคชันทำซ้ำหลายพันครั้งต่อวินาที เช่น การอ่านข้อมูลผู้ใช้ การอัปเดตเซสชัน หรือการเพิ่มคำสั่งซื้อ
DuckDB เขียนข้อมูลแต่ละคอลัมน์แยกกันและทำการบีบอัดข้อมูล การหาผลรวม amount_cents จากข้อมูล 5 ล้านแถวจะอ่านเฉพาะคอลัมน์ amount_cents เท่านั้น โดยจะข้ามไบต์อื่นทั้งหมดในไฟล์และประมวลผลผลรวมผ่านโค้ดแบบ vectorised บนชุดข้อมูลแต่ละกลุ่ม คอลัมน์อื่นจะไม่ถูกอ่านจากดิสก์เลย ซึ่งนี่คือที่มาของความเร็ว
หากนำเอนจินแต่ละตัวไปทำงานในรูปแบบของอีกตัวหนึ่ง SQLite ที่ต้องหาผลรวมของคอลัมน์จะต้องไล่ดูทุกแถวและดึงข้อมูลทั้งแถวออกมาจากหน้าข้อมูลเพื่อเข้าถึงฟิลด์เดียว ทำให้ต้องอ่านดิสก์มากกว่าที่จำเป็นมาก ส่วน DuckDB ที่ต้องเพิ่มคำสั่งซื้อหนึ่งรายการจะต้องเข้าถึงพื้นที่จัดเก็บของทุกคอลัมน์เพื่อใส่ค่าเพียงค่าเดียว และต้องใช้ write lock กับไฟล์ฐานข้อมูลทั้งหมดเพื่อดำเนินการดังกล่าว ทั้งสองเอนจินไม่ได้ทำงานผิดพลาด แต่ละตัวกำลังตอบโจทย์ในรูปแบบที่ไม่ได้ถูกออกแบบมาให้รองรับ
จุดแข็งของ SQLite: สถานะของแอปพลิเคชันแบบ transactional
เลือกใช้ SQLite เมื่อมีการเขียนข้อมูลขนาดเล็กที่เกิดขึ้นบ่อยครั้งและห้ามสูญหาย เช่น ข้อมูล session, รายการสั่งซื้อ, แถวในคิว, การตั้งค่า หรือข้อมูลใดๆ ที่คำขอเว็บสร้างขึ้น
sudo apt update && sudo apt install -y sqlite3
sudo install -d -o "$USER" -g "$USER" /srv/appสร้างตารางและเปิดใช้งาน write-ahead logging ในขั้นตอนเดียวกัน
sqlite3 /srv/app/app.db <<'SQL'
PRAGMA journal_mode = WAL;
CREATE TABLE IF NOT EXISTS orders (
id INTEGER PRIMARY KEY,
customer TEXT NOT NULL,
placed_at TEXT NOT NULL,
amount_cents INTEGER NOT NULL
);
CREATE INDEX IF NOT EXISTS orders_customer ON orders (customer);
INSERT INTO orders (customer, placed_at, amount_cents)
VALUES ('ana', '2026-07-30T09:14:00Z', 4200);
SQLบรรทัดแรกของผลลัพธ์คือ wal ซึ่งเป็นการที่ PRAGMA รายงานโหมดที่เปลี่ยนไป และถือเป็นการตั้งค่าที่มีประโยชน์ที่สุดบนเซิร์ฟเวอร์ ในโหมด rollback journal แบบปกติ ผู้เขียนจะบล็อกผู้อ่านทุกคน แต่ในโหมด WAL ผู้อ่านจะยังคงอ่านสถานะล่าสุดที่ commit ไว้ได้ในขณะที่ผู้เขียนกำลังต่อท้ายข้อมูล ดังนั้นรายงานที่ทำงานช้าจึงไม่ทำให้คำขอเว็บที่รออยู่ต้องหยุดชะงักอีกต่อไป
ตรวจสอบว่าแถวข้อมูลถูกบันทึกกลับมาแล้ว:
sqlite3 /srv/app/app.db "SELECT * FROM orders WHERE customer = 'ana';"คุณจะได้รับ 1|ana|2026-07-30T09:14:00Z|4200 และมีไฟล์เพิ่มขึ้นมาอีก 2 ไฟล์ข้างฐานข้อมูล คือ app.db-wal และ app.db-shm ซึ่งทั้งคู่เป็นส่วนหนึ่งของฐานข้อมูล การคัดลอกเฉพาะ app.db ในขณะที่แอปพลิเคชันกำลังทำงานจะทำให้ได้ไฟล์สำรองที่ไม่สมบูรณ์ ซึ่งจะมีการอธิบายรายละเอียดเพิ่มเติมในส่วนถัดไป
SQLite ยังคงอนุญาตให้มีผู้เขียนได้เพียงหนึ่งรายในเวลาเดียวกัน ข้อจำกัดนี้เป็นรูปแบบการล็อกไม่ใช่คิว ดังนั้นผู้เขียนรายที่สองที่รอเป็นเวลานานเกินไปจะล้มเหลวด้วยข้อผิดพลาด database is locked แทนที่จะรอค้างไปตลอดกาล คุณสามารถเพิ่มระยะเวลารอคอยได้ด้วย PRAGMA busy_timeout = 5000; ในทุกการเชื่อมต่อที่แอปพลิเคชันของคุณเปิดขึ้น การตั้งค่าระยะเวลารอ 5 วินาทีจะช่วยลดข้อผิดพลาดเหล่านี้ได้เกือบทั้งหมดสำหรับภาระงานเว็บทั่วไป
จุดแข็งของ DuckDB: การวิเคราะห์ข้อมูลบนไฟล์ที่คุณมีอยู่แล้ว
เลือกใช้ DuckDB เมื่อคำถามขึ้นต้นด้วย "เท่าไหร่", "จำนวนเท่าใด" หรือ "สิบอันดับแรกคืออะไร" และข้อมูลนำเข้าเป็นไฟล์ CSV หรือ Parquet จำนวนมาก ให้ติดตั้ง command line client เวอร์ชัน 1.5.5 (ข้อมูล ณ เดือนกรกฎาคม 2026):
curl https://install.duckdb.org | shสคริปต์จะติดตั้ง binary ไว้ที่ ~/.duckdb/cli/latest/duckdb และแสดงบรรทัดคำสั่งสำหรับเพิ่มลงใน PATH ของคุณ จากนั้นตรวจสอบการทำงาน:
~/.duckdb/cli/latest/duckdb :memory: "SELECT version();"สร้างไฟล์ตัวอย่างที่สมจริงเพื่อใช้คิวรี คำสั่งนี้จะเขียนข้อมูลคำสั่งซื้อจำนวน 5 ล้านแถวลงในไฟล์ Parquet โดยบีบอัดด้วย zstd:
mkdir -p /srv/data
duckdb :memory: "
COPY (
SELECT i AS id,
'cust_' || (i % 5000) AS customer,
TIMESTAMP '2026-01-01 00:00:00' + INTERVAL (i) MINUTE AS placed_at,
(i * 37) % 20000 AS amount_cents
FROM range(5000000) t(i)
) TO '/srv/data/orders.parquet' (FORMAT parquet, COMPRESSION zstd);"ตอนนี้ให้เริ่มตั้งคำถามเชิงวิเคราะห์ เปิด shell ขึ้นมา เปิดใช้งานตัวจับเวลา และคิวรีไฟล์โดยตรงโดยไม่ต้องผ่านขั้นตอนการนำเข้าข้อมูล:
.timer on
SELECT customer,
count(*) AS orders,
sum(amount_cents)/100.0 AS revenue
FROM '/srv/data/orders.parquet'
GROUP BY customer
ORDER BY revenue DESC
LIMIT 5;ให้อ่านตัวเลขจาก .timer ของคุณเองแทนที่จะเชื่อตัวเลขที่เผยแพร่ทั่วไป เพราะผลลัพธ์ขึ้นอยู่กับความเร็วของดิสก์และจำนวนคอร์ที่คุณมี สิ่งที่สำคัญคือรูปแบบการทำงานของมัน ไม่มีการใช้ CREATE TABLE, ไม่มีการใช้ INSERT และไม่มีขั้นตอนการโหลดข้อมูล: DuckDB จะอ่านส่วนท้าย (footer) ของไฟล์ Parquet เพื่อระบุว่าคิวรีต้องการ column chunks ส่วนใดบ้าง แล้วจึงอ่านเฉพาะส่วนนั้นเท่านั้น การทำงานกับไฟล์ทั้งไดเรกทอรีสามารถทำได้ในลักษณะเดียวกันโดยใช้ glob หรือ FROM '/srv/data/orders-*.parquet' ซึ่งเป็นวิธีที่ทำให้ข้อมูลที่ส่งออกรายวันตลอดทั้งเดือนกลายเป็นคิวรีเดียวได้
ความเร็วของดิสก์คือปัจจัยพื้นฐานของกระบวนการทั้งหมดนี้ และการสแกนคอลัมน์เป็นการอ่านแบบลำดับ (sequential read) ดังนั้นช่องว่างระหว่าง NVMe กับที่เก็บข้อมูล SATA แบบเก่าบน VPS จึงปรากฏให้เห็นชัดเจนกว่าการอ่านแบบสุ่มขนาดเล็กของ SQLite
การอ่านฐานข้อมูล SQLite จาก DuckDB
ทั้งสองเอนจินสามารถทำงานร่วมกันได้ผ่านส่วนขยาย sqlite ของ DuckDB ให้ทำการ attach ฐานข้อมูลของแอปพลิเคชันในโหมดอ่านอย่างเดียว เพื่อให้มั่นใจว่าการคิวรีเพื่อวิเคราะห์ข้อมูลจะไม่สามารถเขียนทับสถานะปัจจุบันได้:
INSTALL sqlite;
LOAD sqlite;
ATTACH '/srv/app/app.db' AS app (TYPE sqlite, READ_ONLY);
SELECT customer, count(*) AS orders
FROM app.orders
GROUP BY customer
ORDER BY orders DESC;คำสั่งนี้จะอ่านแถวข้อมูลจากไฟล์ SQLite ในขณะที่คิวรีทำงานโดยไม่มีการคัดลอกข้อมูล วิธีนี้สะดวกแต่ไม่ได้มีความเร็วสูง เนื่องจากข้อมูลบนดิสก์ยังคงจัดเก็บในรูปแบบ row storage และ DuckDB จำเป็นต้องไล่อ่านข้อมูลทีละแถว ควรใช้สำหรับการส่งออกข้อมูล (export) เท่านั้น ไม่ควรใช้กับแดชบอร์ดที่มีการโหลดข้อมูลใหม่ทุก 30 วินาที:
COPY (SELECT * FROM app.orders)
TO '/srv/data/orders-2026-07.parquet' (FORMAT parquet, COMPRESSION zstd);คำสั่งเดียวนี้คือรูปแบบการทำงานทั้งหมด SQLite จะเป็นผู้ดูแลแถวข้อมูลล่าสุดที่ใช้งานจริง ส่วนการส่งออกข้อมูลตามกำหนดการจะเปลี่ยนข้อมูลในช่วงเวลาที่ปิดรอบแล้วให้เป็นรูปแบบ Parquet โดย DuckDB จะทำหน้าที่ตอบคำถามทุกอย่างที่ครอบคลุมระยะเวลาหลายเดือน ในขณะที่ฐานข้อมูลของแอปพลิเคชันจะมีขนาดเล็ก ซึ่งช่วยให้การเขียนข้อมูลยังคงทำได้อย่างรวดเร็ว
ควรดำเนินการส่งออกข้อมูลตามกำหนดการแทนการทำด้วยตนเอง การใช้ คู่ของ systemd service และ timer เป็นขนาดที่เหมาะสมสำหรับงานนี้ โดยมีหนึ่ง unit สำหรับรัน COPY และหนึ่ง timer สำหรับสั่งให้ทำงานทุกคืน
การรันทั้งสองอย่างบน VPS เดียวกัน
ไม่มีส่วนใดที่จำเป็นต้องใช้ container หรือพอร์ต ทั้งสอง engine เป็นเพียงไลบรารี การติดตั้งจึงเป็นเพียงการติดตั้งแพ็กเกจและระบุ path ของไฟล์ หาก stack ส่วนที่เหลือของคุณรันอยู่บน Docker Compose บน VPS เดียวกัน ให้ mount directory ข้อมูลเข้าไปใน container ที่ต้องการใช้งานแทนการเพิ่ม service ฐานข้อมูล เนื่องจากไม่มี service ใดให้เพิ่ม
มีกฎ 2 ข้อที่จะช่วยให้การจัดการนี้ไม่มีปัญหา
กำหนด directory แยกสำหรับแต่ละ engine: /srv/app สำหรับไฟล์ SQLite ที่แอปพลิเคชันเขียน และ /srv/data สำหรับไฟล์ Parquet ที่ระบบวิเคราะห์อ่าน หากใช้ directory ร่วมกัน งานสำรองข้อมูลที่ทำ snapshot ของไฟล์หนึ่งอาจเกิดการแย่งชิงทรัพยากรกับอีกไฟล์หนึ่งได้
ห้ามกำหนดให้สอง process เข้าถึงไฟล์ฐานข้อมูล DuckDB เดียวกันในโหมดอ่าน-เขียนพร้อมกัน มีเพียง process เดียวเท่านั้นที่สามารถถือครองไฟล์ DuckDB เพื่อเขียนข้อมูลได้ และ process ที่สองจะเปิดไฟล์ไม่ได้เลย การมีผู้อ่านหลายรายสามารถทำได้หากทุกรายตั้งค่า access_mode = 'READ_ONLY' สิ่งนี้มักทำให้ผู้ที่ย้ายมาจาก SQLite ประหลาดใจ เนื่องจากใน SQLite หลาย process สามารถแชร์ไฟล์ร่วมกันได้ตามปกติ หากระบบวิเคราะห์ของคุณอ่านเฉพาะไฟล์ Parquet ปัญหานี้จะไม่เกิดขึ้น ซึ่งเป็นอีกหนึ่งเหตุผลที่ควรเก็บสถานะที่ต้องการความคงทนไว้ใน SQLite
การสำรองข้อมูลมีความแตกต่าง และความแตกต่างนั้นส่งผลกระทบ
ฐานข้อมูล SQLite ที่กำลังทำงานอยู่ประกอบด้วยไฟล์ 3 ไฟล์ และการคัดลอกไฟล์เหล่านั้นด้วย cp ในระหว่างที่มีการเขียนข้อมูลจะทำให้ได้ไฟล์ที่เปิดได้แต่ข้อมูลไม่ถูกต้อง ให้ใช้คำสั่งสำรองข้อมูลของตัวเอนจินเอง ซึ่งจะทำการสร้าง snapshot ที่มีความสอดคล้องกันในขณะที่แอปพลิเคชันยังคงเขียนข้อมูลต่อไปได้:
sqlite3 /srv/app/app.db ".backup '/srv/backup/app-$(date -u +%Y%m%dT%H%M%SZ).db'"
sqlite3 /srv/backup/app-20260730T091400Z.db "PRAGMA integrity_check;"integrity_check จะแสดงผล ok หากการคัดลอกเสร็จสมบูรณ์ หากเป็นค่าอื่น ให้ทิ้ง snapshot นั้นและเริ่มทำการสำรองข้อมูลใหม่
ไฟล์ Parquet จะไม่มีการเปลี่ยนแปลงหลังจากถูกเขียนลงดิสก์แล้ว จึงไม่จำเป็นต้องจัดการเป็นพิเศษ ให้สำรองข้อมูลทั้งไดเรกทอรีได้เลย ส่งทั้งสองพาธออกจากเซิร์ฟเวอร์ด้วย การสำรองข้อมูล restic จาก VPS ของคุณ แล้วเลเยอร์ข้อมูลทั้งหมดของคุณจะกลายเป็นเพียงสองไดเรกทอรีในงานสำรองข้อมูลเดียว
รูปแบบความล้มเหลวและข้อความที่คุณจะพบ
Error: database is locked จาก SQLite หมายความว่ามีการเชื่อมต่ออื่นถือครอง write lock ไว้นานเกินกว่าที่ค่า timeout ของคุณจะอนุญาต ซึ่งไม่ใช่ปัญหาไฟล์เสียหาย ให้ตั้งค่า PRAGMA busy_timeout ในทุกการเชื่อมต่อ จากนั้นตรวจสอบหา transaction ที่ทำงานยาวนานเกินไปซึ่งควรจะแบ่งเป็น transaction ย่อยหลายรายการ
Error: unable to open database file หลังจากเปลี่ยนสิทธิ์การเข้าถึงไฟล์ มักหมายความว่า process สามารถเขียนไฟล์ได้แต่ไม่สามารถเขียนในไดเรกทอรีของไฟล์นั้นได้ SQLite จะสร้างไฟล์ app.db-wal และ app.db-shm ไว้ข้างๆ กับไฟล์ฐานข้อมูล ดังนั้นไดเรกทอรีดังกล่าวจะต้องได้รับสิทธิ์เขียน ไม่ใช่แค่ตัวไฟล์ .db เท่านั้น
IO Error: Could not set lock on file จาก DuckDB หมายความว่ามี process อื่นเปิดฐานข้อมูลนั้นเพื่อเขียนอยู่แล้ว ให้ปิด shell อื่น หรือเปิดฐานข้อมูลของคุณในโหมดอ่านอย่างเดียว (read only)
Out of Memory Error จาก DuckDB บน VPS ขนาดเล็ก หมายความว่า query ต้องการหน่วยความจำขณะทำงาน (working memory) มากกว่าที่มีอยู่ DuckDB จะพยายามเขียนข้อมูลลงดิสก์เมื่อทำได้ ดังนั้นควรจัดเตรียมพื้นที่สำหรับเขียนข้อมูลชั่วคราวโดยการเปิดไฟล์ฐานข้อมูลบนดิสก์แทนการใช้ :memory: และจำกัดการใช้หน่วยความจำด้วย SET memory_limit = '2GB'; สำหรับเครื่องที่รันบริการอื่นอยู่ด้วย การจำกัดค่านี้จะช่วยป้องกันไม่ให้ query ที่รันขึ้นมาเฉพาะกิจแย่งใช้ RAM จนแอปพลิเคชันหลักของคุณทำงานไม่ได้
Binder Error: Referenced column "amount" not found เมื่อ query ข้อมูลจาก Parquet มักหมายความว่า schema ของไฟล์ไม่ตรงกับที่คุณเข้าใจ ให้รันคำสั่ง DESCRIBE SELECT * FROM '/srv/data/orders.parquet'; เพื่อตรวจสอบชื่อคอลัมน์ที่แท้จริงอีกครั้ง
วิธีการเลือกใช้งานจริง
ให้พิจารณารูปแบบการเขียนข้อมูล หากมีการเขียนข้อมูลขนาดเล็กจำนวนมากที่ต้องคงอยู่แม้เกิดเหตุการณ์ไฟฟ้าดับ SQLite คือตัวเลือกที่เหมาะสม จากนั้นให้พิจารณารูปแบบการอ่านข้อมูล หากต้องการการสแกนข้อมูลทั้งหมดพร้อมการคำนวณสรุปผลจากประวัติข้อมูลระยะยาว DuckDB คือตัวเลือกที่เหมาะสม ระบบส่วนใหญ่ในความเป็นจริงมักต้องการทั้งสองอย่าง คำตอบที่ถูกต้องคือการแบ่งงานให้แต่ละเอนจินจัดการในส่วนที่ตนเองถนัด แทนที่จะบังคับให้เอนจินใดเอนจินหนึ่งทำงานทั้งหมด
สิ่งที่ควรหลีกเลี่ยงคือการย้ายสถานะของแอปพลิเคชันที่กำลังทำงานอยู่ (live application state) ไปยัง DuckDB เพียงเพราะรายงานทำงานช้า สาเหตุที่รายงานทำงานช้าเกิดจากรูปแบบการจัดเก็บข้อมูล (storage layout) ดังนั้นวิธีแก้ไขที่ถูกต้องคือการส่งออกข้อมูล (export) ไม่ใช่การเขียนเส้นทางการเขียนข้อมูล (write path) ของคุณใหม่
FAQ
DuckDB สามารถใช้แทน SQLite สำหรับฐานข้อมูลแอปพลิเคชันของฉันได้หรือไม่?
ไม่ได้ หากแอปพลิเคชันของคุณมีการเขียนข้อมูลบ่อยครั้ง DuckDB จะล็อกไฟล์ฐานข้อมูลทั้งหมดสำหรับการเขียน โดยอนุญาตให้มีกระบวนการอ่าน-เขียนได้เพียงหนึ่งรายการในขณะนั้น และถูกปรับแต่งมาเพื่อการเปลี่ยนแปลงข้อมูลจำนวนมาก (bulk changes) มากกว่าการแทรกข้อมูลทีละแถว ให้เก็บสถานะธุรกรรมไว้ใน SQLite แล้วใช้ ATTACH '/srv/app/app.db' AS app (TYPE sqlite, READ_ONLY); ให้ DuckDB อ่านข้อมูลเมื่อต้องการทำรายงาน
DuckDB เร็วกว่า SQLite สำหรับงานวิเคราะห์ข้อมูลจริงหรือไม่?
สำหรับการสแกนและรวมข้อมูลในตารางขนาดใหญ่ คำตอบคือใช่ เหตุผลมาจากรูปแบบการจัดเก็บข้อมูล ไม่ใช่เทคนิคการปรับแต่ง DuckDB จะอ่านเฉพาะคอลัมน์ที่ระบุในคำสั่ง query และประมวลผลค่าเป็นชุด (batches) ในขณะที่ SQLite ต้องไล่ดูทั้งแถวเพื่อเข้าถึงฟิลด์เดียว สำหรับการดึงข้อมูลทีละแถวโดยใช้ primary key ผลลัพธ์จะกลับกัน เพราะ SQLite เข้าถึงเพียงสองหน้า (pages) แต่ DuckDB ต้องเข้าถึงพื้นที่จัดเก็บของทุกคอลัมน์
ฉันต้องใช้ RAM จำนวนมากเพื่อรัน DuckDB บน VPS หรือไม่?
ไม่จำเป็น แต่ควรจำกัดการใช้งานและใช้พื้นที่ดิสก์ร่วมด้วย ให้เปิดไฟล์ฐานข้อมูลแทนการใช้ :memory: เพื่อให้ DuckDB สามารถเขียนผลลัพธ์ชั่วคราวลงดิสก์ได้ จากนั้นตั้งค่า SET memory_limit = '2GB'; ให้เป็นค่าที่ VPS ของคุณรองรับได้ หากไม่มีการจำกัดค่านี้ คำสั่ง GROUP BY ขนาดใหญ่เพียงคำสั่งเดียวอาจทำให้เกิด Out of Memory Error หรือแย่ง RAM ไปจากบริการอื่นจนหมด
ฉันจะนำข้อมูลจาก SQLite ไปเป็น Parquet ได้อย่างไร?
ให้เชื่อมต่อไฟล์ SQLite จาก DuckDB แล้วคัดลอกผลลัพธ์จาก query โดยตรงด้วย COPY (SELECT * FROM app.orders) TO '/srv/data/orders.parquet' (FORMAT parquet, COMPRESSION zstd); ให้รันคำสั่งนี้ตามกำหนดเวลาสำหรับช่วงเวลาที่ปิดรอบแล้ว เช่น ข้อมูลของเดือนที่ผ่านมา และเก็บข้อมูลล่าสุดไว้ใน SQLite ซึ่งเป็นที่ที่แอปพลิเคชันยังคงเขียนข้อมูลอยู่
ฉันควรสำรองข้อมูลตัวไหน และทำอย่างไร?
ควรสำรองทั้งสองตัวด้วยวิธีที่ต่างกัน ให้ทำ snapshot ของ SQLite ด้วย sqlite3 app.db ".backup '/srv/backup/app.db'" แทนการใช้ cp เพราะฐานข้อมูลที่กำลังทำงานอยู่จะมีสถานะเป็น -wal และไฟล์ -shm ซึ่งการคัดลอกไฟล์ตรงๆ อาจทำให้ข้อมูลเสียหายได้ ส่วนไฟล์ Parquet จะไม่มีการเปลี่ยนแปลงเมื่อเขียนเสร็จแล้ว ดังนั้นการคัดลอกทั้งไดเรกทอรีจึงเพียงพอแล้ว