Server वर DuckDB की SQLite? प्रत्यक्ष उत्तर दोन्ही
SQLite transactional application state साठवते, तर DuckDB Parquet आणि CSV वर analytics करते. एका VPS वर दोन्ही का चालतात, हे worked example सह जाणून घ्या.
DuckDB विरुद्ध SQLite on a server: एका वाक्यातील उत्तर
SQLite हे OLTP engine (online transaction processing) आहे: ते डेटा rows म्हणून साठवते आणि एकावेळी काही rows सुरक्षितपणे व जलद वाचण्यासाठी आणि लिहिण्यासाठी तयार केलेले आहे. DuckDB हे OLAP engine (online analytical processing) आहे: ते डेटा columns म्हणून साठवते आणि लाखो rows scan करून एक aggregate परत करण्यासाठी तयार केलेले आहे. दोन्ही embedded libraries आहेत, दोन्ही plain file उघडतात आणि दोन्हींसाठी देखरेख करावी लागणारी server process चालत नाही.
म्हणून “which one” या प्रश्नाचे प्रामाणिक उत्तर जवळजवळ नेहमी “दोन्ही, त्याच VPS वर” असे असते. तुमचे application त्याची live state SQLite मध्ये ठेवते. तुमचे reporting DuckDB वापरून Parquet आणि CSV files वाचते. ती एकमेकांशी स्पर्धा करत नाहीत, कारण दोन्ही एकच काम करत नाहीत.
पंक्ती-आधारित आणि स्तंभ-आधारित संचयनामुळे उत्तर का बदलते
SQLite पृष्ठातील एक पंक्ती सलग भाग म्हणून लिहितो. प्राथमिक कीनुसार एक order मिळवताना एक index page आणि एक data page वाचावा लागतो. म्हणजेच दोन reads होतात. अॅप्लिकेशन दर सेकंदाला हजारो वेळा नेमके हेच करते: हा user वाचा, हे session update करा, हा order insert करा.
DuckDB प्रत्येक column स्वतंत्रपणे लिहितो आणि त्याचे compression करतो. पाच million rows मधील amount_cents ची बेरीज करताना फक्त amount_cents column वाचावा लागतो. फाइलमधील इतर प्रत्येक byte वगळला जातो आणि values च्या batches वर vectorised code वापरून बेरीज केली जाते. इतर columns disk वरून कधीही वाचले जात नाहीत. वेग मिळण्याचे कारण हेच आहे.
आता प्रत्येक engine दुसऱ्या engine च्या workload वर चालवा. SQLite मध्ये एखाद्या column ची बेरीज करण्यासाठी प्रत्येक row मधून जावे लागते. एका field पर्यंत पोहोचण्यासाठी संपूर्ण row page वरून घ्यावी लागते. त्यामुळे आवश्यकतेपेक्षा खूप जास्त disk वाचला जातो. DuckDB मध्ये एक order insert करताना एका value साठी प्रत्येक column च्या storage ला स्पर्श करावा लागतो. तसेच त्यासाठी संपूर्ण database file वर write lock घेतला जातो. कोणताही engine खराब नाही. प्रत्येक engine ज्या कामासाठी तयार केलेला नाही, त्या कामाचे उत्तर देत आहे.
SQLite कुठे उपयुक्त ठरते: व्यवहाराधारित अॅप्लिकेशन स्थिती
लेखनक्रिया लहान, वारंवार होणाऱ्या आणि गमावता कामा नयेत अशा असतील, तर SQLite निवडा. Sessions, orders, queue rows, settings आणि web request मुळे तयार होणारी कोणतीही माहिती यासाठी योग्य आहे.
sudo apt update && sudo apt install -y sqlite3
sudo install -d -o "$USER" -g "$USER" /srv/appTable तयार करा आणि त्याच टप्प्यात 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);
SQLOutput ची पहिली ओळ wal असते. ही मोड बदलल्याची माहिती देणारी PRAGMA आहे आणि server वर सर्वाधिक उपयुक्त setting आहे. Default rollback journal mode मध्ये writer प्रत्येक reader ला block करतो. WAL mode मध्ये एक writer append करत असताना readers शेवटची commit झालेली स्थिती वाचत राहतात. त्यामुळे slow report मुळे त्यामागील web request आता अडकत नाही.
Row परत आली आहे का ते तपासा:
sqlite3 /srv/app/app.db "SELECT * FROM orders WHERE customer = 'ana';"तुम्हाला 1|ana|2026-07-30T09:14:00Z|4200 मिळते. Database च्या शेजारी आणखी दोन files तयार झाल्या आहेत: app.db-wal आणि app.db-shm. दोन्ही files त्याच database च्या आहेत. Application चालू असताना केवळ app.db ची copy केल्यास backup अपूर्ण राहतो. याचे स्पष्टीकरण पुढे दिले आहे.
SQLite मध्ये एका वेळी फक्त एक writer कार्य करू शकतो. ही मर्यादा lock आहे, queue नाही. त्यामुळे दुसरा writer खूप वेळ प्रतीक्षा करत असल्यास तो कायम block न होता database is locked सह fail होतो. Application उघडत असलेल्या प्रत्येक connection वर PRAGMA busy_timeout = 5000; वापरून प्रतीक्षा कालावधी वाढवा. सामान्य web workload मध्ये पाच सेकंदांची प्रतीक्षा या errors पैकी बहुतेक errors टाळते.
फाइल्सवर आधीपासून असलेल्या डेटाचे analytics: DuckDB कुठे उपयुक्त ठरते
प्रश्नाची सुरुवात "किती", "एकूण किती" किंवा "सर्वोच्च दहा कोणते" यापैकी एखाद्याने होत असेल आणि input मध्ये अनेक CSV किंवा Parquet files असतील, तर DuckDB निवडा. command line client install करा. July 2026 नुसार version 1.5.5 आहे:
curl https://install.duckdb.org | shहे script binary ~/.duckdb/cli/latest/duckdb अंतर्गत install करते आणि ते तुमच्या PATH मध्ये समाविष्ट करणारी line print करते. ते चालते का ते तपासा:
~/.duckdb/cli/latest/duckdb :memory: "SELECT version();"Query करण्यासाठी वास्तववादी file तयार करा. हे zstd ने compressed असलेली orders ची पाच million rows असलेली Parquet file लिहिते:
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);"आता analytical question विचारा. Shell उघडा, timer सुरू करा आणि कोणताही import step न करता file थेट query करा:
.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 वरून वाचा, कारण result तुमच्या disk आणि core count वर अवलंबून असतो. त्याचा आकार महत्त्वाचा आहे. येथे CREATE TABLE नव्हते, INSERT नव्हते आणि load stepही नव्हता: DuckDB ने Parquet footer वाचला, query ला कोणते column chunks आवश्यक आहेत ते ठरवले आणि फक्त तेच वाचले. glob, FROM '/srv/data/orders-*.parquet' वापरून संपूर्ण directory वरही हीच पद्धत लागू होते. त्यामुळे महिनाभरातील daily exports एकाच query मध्ये वापरता येतात.
या सर्व प्रक्रियेचा पाया disk speed आहे. Column scan म्हणजे लांब sequential read असतो. त्यामुळे VPS वरील NVMe आणि जुन्या SATA storage मधील फरक येथे SQLite च्या छोट्या random reads पेक्षा अधिक स्पष्टपणे दिसतो.
DuckDB मधून SQLite database वाचणे
DuckDB च्या sqlite extension द्वारे ही दोन engines एकमेकांशी जोडली जातात. Analytics query live state मध्ये कधीही write करू नये यासाठी application database read only म्हणून 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;यामुळे प्रत्येक query वेळी SQLite file मधून rows वाचल्या जातात आणि कोणतीही copy तयार होत नाही. ही पद्धत सोयीची आहे; परंतु ती fast नाही, कारण disk वरील data अजूनही row storage मध्ये आहे आणि DuckDB ला तो पूर्णपणे scan करावा लागतो. ती export साठी वापरा; दर तीस सेकंदांनी reload होणाऱ्या dashboard साठी वापरू नका:
COPY (SELECT * FROM app.orders)
TO '/srv/data/orders-2026-07.parquet' (FORMAT parquet, COMPRESSION zstd);हा एकच statement संपूर्ण pattern दाखवतो. अलीकडील live rows ची मालकी SQLite कडे राहते. Scheduled export मुळे बंद झालेला कालावधी Parquet मध्ये रूपांतरित होतो. अनेक महिन्यांवर आधारित प्रत्येक query चे उत्तर DuckDB देते. Application database लहान राहते, त्यामुळे त्याचे writes fast राहतात.
Export manually करण्याऐवजी तो schedule वर चालवा. यासाठी एक systemd service आणि timer ची जोडी योग्य आहे: एक unit COPY चालवते आणि एक timer तो nightly सुरू करतो.
एकाच VPS वर दोन्ही चालवणे
येथे कंटेनरची आवश्यकता नाही आणि कोणत्याही पोर्टचीही आवश्यकता नाही. दोन्ही इंजिन ही libraries आहेत. त्यामुळे installation म्हणजे package आणि file path एवढेच असते. तुमच्या stack मधील इतर सेवा त्याच VPS वर Docker Compose अंतर्गत आधीच चालत असतील, तर database service जोडण्याऐवजी आवश्यक असलेल्या container मध्ये data directory mount करा. याचे कारण म्हणजे येथे जोडण्यासाठी कोणतीही service नसते.
ही रचना अडचणींपासून सुरक्षित ठेवण्यासाठी दोन नियम पाळा.
प्रत्येक engine साठी स्वतंत्र directory द्या: application ज्या SQLite file मध्ये लिहिते त्यासाठी /srv/app आणि analytics ज्या Parquet files वाचते त्यासाठी /srv/data. दोन्हींसाठी एकच directory वापरल्यास, एका directory चा snapshot घेणारे backup job आणि दुसरीकडील प्रक्रिया एकमेकांशी स्पर्धा करू शकतात.
दोन processes ना एकाच DuckDB database file कडे read-write mode मध्ये निर्देशित करू नका. DuckDB file लेखनासाठी एकावेळी फक्त एक process उघडी ठेवू शकतो. दुसरा process ती file उघडण्यात पूर्णपणे अयशस्वी होतो. प्रत्येक reader ने access_mode = 'READ_ONLY' सेट केले असल्यास अनेक readers चालू शकतात. SQLite वापरून आलेल्या वापरकर्त्यांना हे अनपेक्षित वाटू शकते, कारण SQLite मध्ये अनेक processes नियमितपणे एकच file share करतात. तुमचे analytics फक्त Parquet files वाचत असल्यास हा प्रश्न उद्भवतच नाही. त्यामुळे durable state SQLite मध्ये ठेवण्याचे हे आणखी एक कारण आहे.
बॅकअप वेगवेगळे असतात आणि या फरकामुळे समस्या निर्माण होतात
चालू SQLite database मध्ये तीन files असतात. लेखन सुरू असताना cp वापरून त्यांची copy केल्यास उघडणारी पण चुकीचा data असलेली file तयार होते. त्याऐवजी engine चा स्वतःचा backup command वापरा. Application लेखन सुरू ठेवत असतानाच हा command सुसंगत 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;"यशस्वी copy साठी integrity_check, ok दाखवतो. इतर कोणतेही output आल्यास तो snapshot टाकून द्या आणि दुसरा snapshot घ्या.
Parquet files लिहून झाल्यानंतर बदलत नाहीत. त्यामुळे त्यांच्यासाठी विशेष प्रक्रिया आवश्यक नाही. Directory चा backup घ्या. तुमच्या VPS मधून restic backups वापरून दोन्ही paths server च्या बाहेर पाठवा. अशा प्रकारे संपूर्ण data layer एका backup job मध्ये दोन directories मध्ये सुरक्षित होतो.
अपयशाच्या स्थिती आणि तुम्हाला दिसणारे अचूक संदेश
Error: database is locked हा SQLite कडून येणारा संदेश आहे. याचा अर्थ दुसऱ्या connection ने write lock तुमच्या timeout ने अनुमती दिलेल्या कालावधीपेक्षा जास्त वेळ धरून ठेवला आहे. हा corruption नाही. प्रत्येक connection वर PRAGMA busy_timeout सेट करा. त्यानंतर अनेक लहान transaction असायला हव्या होत्या, पण त्याऐवजी दीर्घकाळ चालणारी transaction कुठे आहे ते शोधा.
परवानग्या बदलल्यानंतर Error: unable to open database file दिसत असल्यास, process फाइलमध्ये write करू शकतो; परंतु तिच्या directory मध्ये write करू शकत नाही, असा त्याचा सामान्य अर्थ असतो. SQLite database च्या शेजारी app.db-wal आणि app.db-shm तयार करते. त्यामुळे फक्त .db फाइल नव्हे, तर directory स्वतः writable असणे आवश्यक आहे.
IO Error: Could not set lock on file हा DuckDB कडून येणारा संदेश आहे. याचा अर्थ दुसऱ्या process ने तो database writing साठी आधीच उघडला आहे. दुसरा shell बंद करा किंवा तुमचा database read only मोडमध्ये उघडा.
लहान VPS वर DuckDB कडून Out of Memory Error दिसत असल्यास, query ला उपलब्ध memory पेक्षा जास्त working memory आवश्यक होती, असा त्याचा अर्थ असतो. शक्य असल्यास DuckDB तात्पुरता data disk वर spill करते. त्यामुळे :memory: ऐवजी disk वरील database file उघडून spill करण्यासाठी जागा द्या आणि SET memory_limit = '2GB'; वापरून memory वापरावर कमाल मर्यादा ठेवा. इतर सेवा चालू असलेल्या मशीनवर ही मर्यादा ad hoc query मुळे तुमचे application RAM मधून बाहेर ढकलले जाण्यापासून रोखते.
Parquet query करताना Binder Error: Referenced column "amount" not found दिसत असल्यास, जवळजवळ नेहमीच फाइलचा schema तुम्हाला आठवत असलेल्या schema पेक्षा वेगळा असतो. DESCRIBE SELECT * FROM '/srv/data/orders.parquet'; चालवा आणि प्रत्यक्ष column names वाचा.
व्यवहारात निवड कशी करावी
लेखनाची पद्धत काय आहे, हे विचारा. वीज खंडित झाल्यानंतरही टिकून राहायला हव्या असलेल्या अनेक लहान लेखनक्रिया असतील, तर SQLite योग्य आहे. वाचनाची पद्धत काय आहे, हे विचारा. दीर्घ इतिहासावर aggregates सह पूर्ण scans करायचे असतील, तर DuckDB योग्य आहे. बहुतेक वास्तविक प्रणाली या दोन्ही प्रश्नांची उत्तरे होय अशी देतात. अशा वेळी एका engine कडून दुसऱ्याचे काम करून घेण्याऐवजी, प्रत्येक engine ला तो ज्या कामासाठी योग्य आहे तो भाग देणे हा योग्य पर्याय असतो.
अहवाल धीमा होता म्हणून live application state DuckDB मध्ये हलवणे हे टाळावे. Storage layout मुळे अहवाल धीमा होता. त्यामुळे उपाय म्हणजे export करणे, तुमचा write path पुन्हा लिहिणे नव्हे.
FAQ
DuckDB माझ्या application database साठी SQLite ची जागा घेऊ शकते का?
वारंवार write करणाऱ्या database साठी नाही. DuckDB संपूर्ण database file वर write lock घेतो, एकावेळी फक्त एका read-write process ला परवानगी देतो आणि single-row inserts ऐवजी bulk changes साठी अनुकूलित आहे. Transactional state SQLite मध्ये ठेवा आणि report आवश्यक असताना DuckDB ला ATTACH '/srv/app/app.db' AS app (TYPE sqlite, READ_ONLY); वापरून तो data वाचू द्या.
Analytics साठी DuckDB खरोखर SQLite पेक्षा वेगवान आहे का?
मोठ्या table वर scans आणि aggregates चालवताना होय. याचे कारण tuning trick नसून storage layout आहे. DuckDB query मध्ये नमूद केलेले columnsच वाचतो आणि values चे batches मध्ये processing करतो. SQLite ला मात्र एखाद्या field पर्यंत पोहोचण्यासाठी संपूर्ण rows तपासाव्या लागतात. Primary key वरून एकच row मिळवताना परिणाम उलट असतो, कारण SQLite दोन pages वाचतो, तर DuckDB प्रत्येक column चे storage वाचतो.
VPS वर DuckDB चालवण्यासाठी मला भरपूर RAM आवश्यक आहे का?
नाही. मात्र त्याला memory limit आणि disk द्या. :memory: ऐवजी database file उघडा, जेणेकरून DuckDB intermediate results disk वर spill करू शकेल. त्यानंतर SET memory_limit = '2GB'; हे तुमचा VPS सहन करू शकेल अशा value वर सेट करा. Limit नसल्यास, एक मोठे GROUP BY Out of Memory Error वाढवू शकते किंवा इतर services ना RAM मधून बाहेर काढू शकते.
माझा SQLite data Parquet मध्ये कसा आणू?
DuckDB मधून SQLite file attach करा आणि COPY (SELECT * FROM app.orders) TO '/srv/data/orders.parquet' (FORMAT parquet, COMPRESSION zstd); वापरून query चा output थेट copy करा. मागील महिन्याच्या rows सारख्या बंद झालेल्या कालावधींसाठी हे schedule वर चालवा. अॅप्लिकेशन अजून write करत असलेल्या अलीकडील rows SQLite मध्येच ठेवा.
यापैकी कोणाचा backup घ्यावा आणि कसा?
दोन्हींचा, परंतु वेगवेगळ्या पद्धतीने. cp ऐवजी sqlite3 app.db ".backup '/srv/backup/app.db'" वापरून SQLite snapshots घ्या, कारण चालू database हा -wal आणि -shm file देखील असतो; साधी copy विस्कळीत होऊ शकते. एकदा लिहिल्यानंतर Parquet files बदलत नाहीत, त्यामुळे directory copy करणे पुरेसे आहे.