SSD Nodes Learn 8GB RAM — $66/वर्ष
मार्गदर्शक Matt Connorद्वारे Matt Connor · अपडेटेड 2026-08-01

सर्व्हरवर DuckDB विरुद्ध SQLite: दोन्ही कधी वापरावे?

SQLite transactional application state साठी, तर DuckDB Parquet आणि CSV वरील analytics साठी वापरा. एका VPS वर दोन्ही का योग्य आहेत, हे उदाहरणांसह समजा.

सर्व्हरवर DuckDB विरुद्ध SQLite: एका वाक्यातील उत्तर

SQLite हे OLTP इंजिन (online transaction processing) आहे: ते डेटा पंक्तींच्या स्वरूपात साठवते आणि एका वेळी काही पंक्ती सुरक्षितपणे व जलद वाचण्यासाठी आणि लिहिण्यासाठी तयार केलेले आहे. DuckDB हे OLAP इंजिन (online analytical processing) आहे: ते डेटा स्तंभांच्या स्वरूपात साठवते आणि लाखो पंक्ती स्कॅन करून एकत्रित परिणाम परत करण्यासाठी तयार केलेले आहे. दोन्ही एम्बेडेड लायब्ररी आहेत, दोन्ही साधी फाइल उघडतात आणि दोन्हींसाठी तुम्हाला सतत देखरेख करावी लागणारी server प्रक्रिया चालवावी लागत नाही.

म्हणून "कोणते?" या प्रश्नाचे प्रामाणिक उत्तर जवळजवळ नेहमीच "त्याच VPS वर दोन्ही" असे असते. तुमचे application त्याची सक्रिय स्थिती SQLite मध्ये ठेवते. तुमचे reporting DuckDB वापरून Parquet आणि CSV फाइल्समधून डेटा वाचते. दोन्ही एकमेकांशी स्पर्धा करत नाहीत, कारण ते एकच काम करत नाहीत.

पंक्ती-आधारित संचयन आणि स्तंभ-आधारित संचयन उत्तर का बदलतात

SQLite पंक्तीला पृष्ठाच्या एका सलग भागात लिहिते. प्राथमिक कळ वापरून एक order आणताना एक index page आणि एक data page वाचावे लागतात, म्हणजे दोन reads होतात. अनुप्रयोग दर सेकंदाला हजारो वेळा नेमके हेच करतो: हा user वाचा, हे session अद्ययावत करा, हा order घाला.

DuckDB प्रत्येक column स्वतंत्रपणे लिहिते आणि त्याचे compression करते. पाच दशलक्ष rows वर amount_cents ची बेरीज करताना ते फक्त amount_cents column वाचते, file मधील इतर प्रत्येक 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 निवडा. सत्रे, ऑर्डर्स, रांगेतील पंक्ती, सेटिंग्ज आणि वेब विनंतीमुळे तयार होणारी कोणतीही माहिती यासाठी योग्य आहे.

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 केलेली स्थिती वाचत राहतात. त्यामुळे संथ रिपोर्टमुळे त्यामागील web विनंती थांबत नाही.

पंक्ती पुन्हा मिळाली आहे का ते तपासा:

sqlite3 /srv/app/app.db "SELECT * FROM orders WHERE customer = 'ana';"

तुम्हाला 1|ana|2026-07-30T09:14:00Z|4200 मिळते. डेटाबेसच्या शेजारी आणखी दोन फाइल्स तयार झाल्या आहेत: app.db-wal आणि app.db-shm. या दोन्ही फाइल्स त्याच डेटाबेसशी संबंधित आहेत. अनुप्रयोग सुरू असताना केवळ app.db ची प्रत घेतल्यास backup अपूर्ण राहतो. याचे वर्णन पुढे केले आहे.

SQLite मध्ये एका वेळी फक्त एक लेखकाला परवानगी असते. ही मर्यादा lock आहे, queue नाही. त्यामुळे खूप वेळ प्रतीक्षा करणारा दुसरा लेखक कायमचा अडून राहत नाही; तो database is locked त्रुटीसह अयशस्वी होतो. अनुप्रयोगाने उघडलेल्या प्रत्येक connection वर PRAGMA busy_timeout = 5000; वापरून प्रतीक्षा कालावधी वाढवा. सामान्य web workload मध्ये 5 सेकंदांची प्रतीक्षा या त्रुटींपैकी बहुतेक त्रुटी दूर करते.

DuckDB कुठे उपयुक्त ठरते: तुमच्याकडे आधीपासून असलेल्या फाइल्सवरील विश्लेषण

प्रश्नाची सुरुवात "किती", "किती प्रमाणात" किंवा "पहिल्या दहा कोणत्या" अशी होत असेल आणि इनपुटमध्ये अनेक CSV किंवा Parquet फाइल्स असतील, तर DuckDB निवडा. कमांड-लाइन क्लायंट install करा. July 2026 पर्यंतची आवृत्ती 1.5.5 आहे:

curl https://install.duckdb.org | sh

ही script binary ~/.duckdb/cli/latest/duckdb अंतर्गत install करते आणि तो तुमच्या PATH मध्ये समाविष्ट करण्यासाठीची ओळ दाखवते. ते चालते याची खात्री करा:

~/.duckdb/cli/latest/duckdb :memory: "SELECT version();"

क्वेरी करण्यासाठी वास्तववादी फाइल तयार करा. ही प्रक्रिया zstd ने compress केलेल्या Parquet मध्ये orders च्या पाच दशलक्ष rows लिहिते:

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 सुरू करा आणि कोणतेही import step न करता फाइलवर थेट 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 मधून वाचा, कारण निकाल तुमच्या 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 डेटाबेस वाचणे

दोन्ही इंजिन DuckDB च्या sqlite extension द्वारे एकत्र काम करतात. Analytics query मुळे live state मध्ये कधीही लेखन होऊ नये म्हणून 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 न करता वाचल्या जातात. ही पद्धत सोयीची आहे, परंतु जलद नाही. कारण disk वरील data अजूनही row storage मध्ये आहे आणि DuckDB ला तो पूर्णपणे तपासावा लागतो. ती export साठी वापरा. दर तीस सेकंदांनी reload होणाऱ्या dashboard साठी वापरू नका:

COPY (SELECT * FROM app.orders)
  TO '/srv/data/orders-2026-07.parquet' (FORMAT parquet, COMPRESSION zstd);

हाच संपूर्ण pattern आहे. SQLite अलीकडील live rows चे व्यवस्थापन करते. Scheduled export बंद झालेल्या कालावधींचे data Parquet मध्ये रूपांतरित करतो. अनेक महिन्यांतील data संदर्भातील प्रत्येक query चे उत्तर DuckDB देते. Application database लहान राहतो, त्यामुळे त्याचे writes जलद होतात.

Export हाताने करण्याऐवजी schedule वर चालवा. यासाठी systemd service आणि timer ची जोडी योग्य आहे: एक unit COPY चालवतो आणि एक timer तो दररोज रात्री सुरू करतो.

एका VPS वर दोन्ही चालवणे

येथे container ची गरज नाही आणि port चीही गरज नाही. दोन्ही engines या 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 दुसऱ्या engine शी स्पर्धा करू लागते.

एकाच DuckDB database file कडे read-write mode मध्ये दोन processes निर्देशित करू नका. DuckDB file मध्ये writing साठी एकावेळी फक्त एक process असू शकतो. दुसरा process ती file उघडण्यातच अयशस्वी होतो. प्रत्येक process ने access_mode = 'READ_ONLY' सेट केले असल्यास अनेक readers चालू शकतात. SQLite वापरून आलेल्या लोकांना हे अनपेक्षित वाटते, कारण SQLite मध्ये अनेक processes नियमितपणे एकाच file चा वापर करतात. तुमचे analytics फक्त Parquet files वाचत असल्यास हा प्रश्नच उद्भवत नाही. त्यामुळे durable state SQLite मध्ये ठेवण्याचे हे आणखी एक कारण आहे.

बॅकअप वेगळे असतात आणि त्यातील फरक गंभीर ठरतो

कार्यरत SQLite डेटाबेसमध्ये तीन फाइल्स असतात. लेखन सुरू असताना cp वापरून त्यांची प्रत घेतल्यास उघडणारी पण चुकीची फाइल तयार होऊ शकते. त्याऐवजी इंजिनची स्वतःची बॅकअप कमांड वापरा. अनुप्रयोग लेखन सुरू ठेवत असतानाच ही कमांड सुसंगत स्नॅपशॉट घेते:

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 प्रिंट करते. इतर कोणताही परिणाम मिळाल्यास तो स्नॅपशॉट टाकून द्या आणि पुन्हा प्रत घ्या.

Parquet फाइल्स लिहून झाल्यानंतर बदलत नाहीत. त्यामुळे त्यांच्यासाठी विशेष प्रक्रिया आवश्यक नाही. डिरेक्टरीचा बॅकअप घ्या. तुमच्या VPS वरील restic बॅकअप वापरून दोन्ही पाथ सर्व्हरबाहेर पाठवा. अशा प्रकारे संपूर्ण डेटा स्तर एका बॅकअप जॉबमधील दोन डिरेक्टरींमध्ये राहतो.

अपयशाच्या स्थिती आणि दिसणारे अचूक स्ट्रिंग

SQLite मधील Error: database is locked याचा अर्थ असा की दुसऱ्या कनेक्शनने write lock तुमच्या timeout मध्ये परवानगी असलेल्या कालावधीपेक्षा जास्त वेळ धरून ठेवला. हा डेटा करप्ट झालेला असल्याचे दर्शवत नाही. प्रत्येक कनेक्शनवर PRAGMA busy_timeout सेट करा. त्यानंतर अनेक लहान transaction असायला हव्या होत्या, पण त्याऐवजी दीर्घ transaction कोणती आहे ते शोधा.

परवानगी बदलल्यानंतर दिसणारे Error: unable to open database file सहसा याचा अर्थ असतो की process फाइलमध्ये write करू शकतो, पण तिच्या directory मध्ये करू शकत नाही. SQLite database च्या शेजारी app.db-wal आणि app.db-shm तयार करते. त्यामुळे फक्त .db फाइलच नव्हे, तर directory देखील writable असणे आवश्यक आहे.

DuckDB मधील IO Error: Could not set lock on file याचा अर्थ असा की दुसऱ्या process ने तो database writing साठी आधीच उघडलेला आहे. दुसरा shell बंद करा किंवा तुमचा database read only मोडमध्ये उघडा.

लहान VPS वर DuckDB मधील Out of Memory Error याचा अर्थ असा की query ला उपलब्ध working memory पेक्षा जास्त memory आवश्यक होती. शक्य असल्यास DuckDB अतिरिक्त data disk वर spill करते. त्यामुळे :memory: ऐवजी disk वरील database file उघडून spill करण्यासाठी जागा द्या आणि SET memory_limit = '2GB'; वापरून memory वापराची कमाल मर्यादा ठरवा. इतर services चालू असलेल्या server वर ही मर्यादा 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 मिळवा.

प्रत्यक्ष वापरात निवड कशी करावी

write pattern काय आहे, हे विचारा. वीज खंडित झाल्यानंतरही टिकून राहायला हवेत असे अनेक छोटे writes असल्यास SQLite निवडा. read pattern काय आहे, हे विचारा. दीर्घ इतिहासावरील aggregates सह full scans आवश्यक असल्यास DuckDB निवडा. बहुतेक वास्तविक systems दोन्ही प्रश्नांना होय असे उत्तर देतात. अशा वेळी एका engine कडून दुसऱ्याचे काम करून घेण्याऐवजी, प्रत्येक engine ज्या कामासाठी योग्य आहे तो भाग त्याला द्या.

report धीमा होता म्हणून live application state DuckDB मध्ये हलवणे ही टाळावी अशी migration आहे. storage layout मुळे report धीमा होता. त्यामुळे उपाय म्हणजे export करणे, तुमचा write path पुन्हा लिहिणे नव्हे.

FAQ

माझ्या अनुप्रयोगाच्या database साठी DuckDB, SQLite ची जागा घेऊ शकते का?

वारंवार लेखन करणाऱ्या 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 आवश्यक आहे का?

नाही, परंतु त्यासाठी एक 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 चे results थेट बाहेर copy करा. मागील महिन्याच्या rows सारख्या बंद कालावधींसाठी हे schedule वर चालवा. अलीकडील rows SQLite मध्येच ठेवा, जिथे application अजूनही त्यात लेखन करते.

कोणता backup घ्यावा आणि तो कसा घ्यावा?

दोन्हींचे, परंतु वेगवेगळ्या पद्धतीने. cp ऐवजी sqlite3 app.db ".backup '/srv/backup/app.db'" वापरून SQLite snapshots घ्या, कारण चालू database हे -wal तसेच -shm file देखील असते आणि साधी copy अपूर्ण होऊ शकते. Parquet files लिहिल्यानंतर कधीही बदलत नाहीत, त्यामुळे directory copy करणे पुरेसे आहे.

#duckdb#sqlite#database#analytics#parquet#self-hosting