SSD Nodes Learn 8GB RAM — $66/साल
गाइड Matt Connorलेखक: Matt Connor · अपडेट किया गया: 2026-08-01

DuckDB बनाम SQLite: सर्वर पर कौन सा डेटाबेस चुनें?

SQLite ट्रांजेक्शनल डेटा के लिए है जबकि DuckDB एनालिटिक्स और Parquet फाइलों के लिए बेहतर है। जानें कि एक ही VPS पर दोनों का उपयोग कैसे करें और इनके बीच का मुख्य तकनीकी अंतर क्या है।

DuckDB बनाम SQLite सर्वर पर: एक वाक्य में उत्तर

SQLite एक OLTP (ऑनलाइन ट्रांजेक्शन प्रोसेसिंग) इंजन है: यह डेटा को पंक्तियों (rows) के रूप में संग्रहीत करता है और इसे एक बार में कुछ पंक्तियों को सुरक्षित और तेज़ी से पढ़ने और लिखने के लिए बनाया गया है। DuckDB एक OLAP (ऑनलाइन एनालिटिकल प्रोसेसिंग) इंजन है: यह डेटा को कॉलम के रूप में संग्रहीत करता है और इसे लाखों पंक्तियों को स्कैन करने और एक एग्रीगेट परिणाम देने के लिए बनाया गया है। दोनों एम्बेडेड लाइब्रेरी हैं, दोनों एक साधारण फ़ाइल खोलते हैं, और दोनों में से कोई भी ऐसा सर्वर प्रोसेस नहीं चलाता है जिसकी आपको निगरानी करनी पड़े।

इसलिए "कौन सा चुनें" का ईमानदार उत्तर लगभग हमेशा "दोनों, एक ही VPS पर" होता है। आपका एप्लिकेशन अपनी लाइव स्थिति को SQLite में रखता है। आपकी रिपोर्टिंग DuckDB के साथ Parquet और CSV फ़ाइलों को पढ़ती है। वे एक-दूसरे के प्रतिस्पर्धी नहीं हैं क्योंकि वे एक ही काम नहीं कर रहे हैं।

रो स्टोरेज और कॉलम स्टोरेज उत्तर को क्यों बदलते हैं

SQLite एक रो को पेज के एक निरंतर हिस्से के रूप में लिखता है। प्राइमरी की द्वारा एक ऑर्डर को फेच करने पर एक इंडेक्स पेज और एक डेटा पेज एक्सेस होता है, जो कि दो रीड्स हैं। एक एप्लिकेशन प्रति सेकंड हजारों बार यही करता है: इस यूजर को पढ़ें, इस सेशन को अपडेट करें, इस ऑर्डर को इंसर्ट करें।

DuckDB प्रत्येक कॉलम को अलग से लिखता है और उसे कंप्रेस करता है। पांच मिलियन रो पर amount_cents का योग करने के लिए केवल amount_cents कॉलम को पढ़ा जाता है, फाइल में बाकी सभी बाइट्स को छोड़ दिया जाता है, और मानों के बैच पर वेक्टरइज्ड कोड के माध्यम से योग की गणना की जाती है। अन्य कॉलम कभी भी डिस्क से नहीं पढ़े जाते, और यही गति का मुख्य कारण है।

अब प्रत्येक इंजन को दूसरे के वर्कलोड पर चलाकर देखें। किसी कॉलम का योग करते समय SQLite को हर रो पर जाना पड़ता है और एक फील्ड तक पहुंचने के लिए पूरी रो को पेज से बाहर निकालना पड़ता है, इसलिए यह आवश्यकता से कहीं अधिक डिस्क रीड करता है। एक ऑर्डर इंसर्ट करते समय DuckDB को एक सिंगल वैल्यू के लिए हर कॉलम के स्टोरेज को एक्सेस करना पड़ता है, और ऐसा करने के लिए यह पूरी डेटाबेस फाइल पर राइट लॉक लगा देता है। इनमें से कोई भी इंजन खराब नहीं है। प्रत्येक उस कार्य को कर रहा है जिसके लिए उसे नहीं बनाया गया था।

SQLite कहाँ बेहतर है: ट्रांजेक्शनल एप्लिकेशन स्टेट

जब राइट्स (writes) छोटे, बार-बार होने वाले हों और उन्हें खोना नहीं चाहिए, तब 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 है जो उस मोड की रिपोर्ट कर रहा है जिसमें उसने स्विच किया है, और यह सर्वर पर सबसे उपयोगी सेटिंग है। डिफ़ॉल्ट रोलबैक जर्नल मोड में, एक राइटर हर रीडर को ब्लॉक कर देता है। WAL मोड में, रीडर्स अंतिम कमिट की गई स्थिति को पढ़ते रहते हैं जबकि एक राइटर डेटा जोड़ता है, इसलिए एक धीमी रिपोर्ट अब उसके पीछे की वेब रिक्वेस्ट को नहीं रोकती है।

जाँचें कि पंक्ति वापस आ गई है:

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 को कॉपी करने से आपको एक अधूरा (torn) बैकअप मिलता है, जिसे आगे विस्तार से समझाया गया है।

SQLite अभी भी एक समय में केवल एक राइटर की अनुमति देता है। वह सीमा एक लॉक है, कतार नहीं, इसलिए दूसरा राइटर जो बहुत देर तक प्रतीक्षा करता है, वह हमेशा के लिए ब्लॉक होने के बजाय database is locked के साथ विफल हो जाता है। अपने एप्लिकेशन द्वारा खोले जाने वाले प्रत्येक कनेक्शन पर PRAGMA busy_timeout = 5000; के साथ प्रतीक्षा समय बढ़ाएँ। पाँच सेकंड का धैर्य सामान्य वेब वर्कलोड पर इन अधिकांश त्रुटियों को दूर कर देता है।

DuckDB कहाँ बेहतर है: आपके पास मौजूद फ़ाइलों पर एनालिटिक्स

जब प्रश्न "कितने", "कितना" या "शीर्ष दस कौन से" से शुरू हो, और इनपुट CSV या Parquet फ़ाइलों का ढेर हो, तो DuckDB चुनें। कमांड लाइन क्लाइंट इंस्टॉल करें, जुलाई 2026 के अनुसार संस्करण 1.5.5:

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

यह स्क्रिप्ट बाइनरी को ~/.duckdb/cli/latest/duckdb के अंतर्गत इंस्टॉल करती है और वह लाइन प्रिंट करती है जो इसे आपके PATH में जोड़ती है। पुष्टि करें कि यह चलता है:

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

क्वेरी करने के लिए एक यथार्थवादी फ़ाइल बनाएँ। यह zstd के साथ संकुचित, ऑर्डर्स की पचास लाख पंक्तियों को Parquet में लिखता है:

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);"

अब विश्लेषणात्मक प्रश्न पूछें। शेल खोलें, टाइमर चालू करें, और बिना किसी इम्पोर्ट चरण के सीधे फ़ाइल को क्वेरी करें:

.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 ने Parquet फुटर को पढ़ा, यह पता लगाया कि क्वेरी को किन कॉलम चंक्स की आवश्यकता है, और केवल उन्हें ही पढ़ा। एक पूरी डायरेक्टरी एक ग्लोब, FROM '/srv/data/orders-*.parquet' के साथ समान रूप से काम करती है, जो कि दैनिक एक्सपोर्ट के एक महीने को एक क्वेरी में बदलने का तरीका है।

डिस्क की गति इन सबके नीचे का आधार है, और कॉलम स्कैन एक लंबा अनुक्रमिक रीड है, इसलिए VPS पर NVMe और पुराने SATA स्टोरेज के बीच का अंतर यहाँ SQLite के छोटे रैंडम रीड्स की तुलना में अधिक स्पष्ट रूप से दिखाई देता है।

DuckDB से अपना SQLite डेटाबेस पढ़ना

ये दोनों इंजन DuckDB के sqlite एक्सटेंशन के माध्यम से जुड़ते हैं। एप्लिकेशन डेटाबेस को केवल-पढ़ने (read-only) के मोड में अटैच करें, ताकि कोई एनालिटिक्स क्वेरी कभी भी लाइव स्टेट में बदलाव न कर सके:

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 फ़ाइल से पंक्तियों (rows) को पढ़ता है। यह सुविधाजनक है लेकिन तेज़ नहीं है, क्योंकि डिस्क पर डेटा अभी भी रो-स्टोरेज (row storage) में है और DuckDB को इसे स्कैन करना पड़ता है। इसका उपयोग एक्सपोर्ट के लिए करें, न कि ऐसे डैशबोर्ड के लिए जो हर तीस सेकंड में रीलोड होता है:

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

यह एक स्टेटमेंट ही पूरा पैटर्न है। SQLite हाल की लाइव पंक्तियों का स्वामी है। एक निर्धारित एक्सपोर्ट बंद हो चुकी अवधियों को Parquet में बदल देता है। DuckDB उन सभी सवालों के जवाब देता है जो महीनों तक फैले होते हैं, और एप्लिकेशन डेटाबेस छोटा रहता है, जिससे इसके राइट्स (writes) तेज़ बने रहते हैं।

एक्सपोर्ट को मैन्युअल रूप से चलाने के बजाय शेड्यूल पर चलाएं। इसके लिए एक systemd सर्विस और टाइमर की जोड़ी सही आकार का समाधान है: एक यूनिट जो COPY को चलाती है, और एक टाइमर जो इसे हर रात सक्रिय करता है।

एक ही VPS पर दोनों को चलाना

यहाँ किसी भी चीज़ के लिए कंटेनर या पोर्ट की आवश्यकता नहीं है। दोनों इंजन लाइब्रेरी हैं, इसलिए इंस्टॉलेशन केवल एक पैकेज और एक फाइल पाथ है। यदि आपका बाकी स्टैक पहले से ही Docker Compose on the same VPS के अंतर्गत चल रहा है, तो डेटा डायरेक्टरी को उस कंटेनर में माउंट करें जिसे इसकी आवश्यकता है, बजाय इसके कि कोई डेटाबेस सर्विस जोड़ी जाए, क्योंकि जोड़ने के लिए कोई सर्विस मौजूद ही नहीं है।

दो नियम इस व्यवस्था को समस्याओं से दूर रखते हैं।

प्रत्येक इंजन को अपनी अलग डायरेक्टरी दें: /srv/app उस SQLite फाइल के लिए जिसे एप्लिकेशन लिखता है, और /srv/data उन Parquet फाइलों के लिए जिन्हें एनालिटिक्स पढ़ता है। जब वे एक ही डायरेक्टरी साझा करते हैं, तो एक बैकअप जॉब जो एक का स्नैपशॉट लेती है, दूसरे के साथ रेस (race) करने लगती है।

दो प्रोसेस को एक ही DuckDB डेटाबेस फाइल पर रीड-राइट मोड में पॉइंट न करें। केवल एक प्रोसेस ही DuckDB फाइल को राइट करने के लिए होल्ड कर सकती है, और दूसरी प्रोसेस इसे ओपन करने में विफल हो जाएगी। जब हर प्रोसेस access_mode = 'READ_ONLY' सेट करती है, तो कई रीडर्स का होना ठीक है। यह उन लोगों को आश्चर्यचकित करता है जो SQLite से आते हैं, जहाँ कई प्रोसेस नियमित रूप से एक फाइल साझा करती हैं। यदि आपका एनालिटिक्स केवल Parquet फाइलें पढ़ता है, तो यह प्रश्न कभी नहीं उठता, जो कि टिकाऊ स्टेट (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 का अर्थ है कि किसी अन्य कनेक्शन ने राइट लॉक को आपके टाइमआउट द्वारा अनुमत समय से अधिक समय तक होल्ड करके रखा है। यह डेटा करप्शन नहीं है। हर कनेक्शन पर PRAGMA busy_timeout सेट करें, और फिर उस लंबे ट्रांजेक्शन को खोजें जिसे कई छोटे ट्रांजेक्शन में विभाजित किया जाना चाहिए था।

अनुमति बदलने के बाद Error: unable to open database file का अर्थ आमतौर पर यह होता है कि प्रोसेस फाइल में तो लिख सकती है लेकिन उसकी डायरेक्टरी में नहीं। SQLite डेटाबेस के बगल में app.db-wal और app.db-shm बनाता है, इसलिए केवल .db फाइल ही नहीं, बल्कि डायरेक्टरी में भी राइट करने की अनुमति होनी चाहिए।

DuckDB से IO Error: Could not set lock on file का अर्थ है कि कोई दूसरी प्रोसेस पहले से ही उस डेटाबेस को राइटिंग के लिए ओपन रखे हुए है। दूसरे शेल को बंद करें, या अपना शेल रीड-ओनली मोड में ओपन करें।

छोटे VPS पर DuckDB से Out of Memory Error का अर्थ है कि किसी क्वेरी को उपलब्ध मेमोरी से अधिक वर्किंग मेमोरी की आवश्यकता थी। DuckDB संभव होने पर डिस्क पर डेटा स्पिल करता है, इसलिए :memory: के बजाय डिस्क पर डेटाबेस फाइल ओपन करके उसे स्पिल करने के लिए जगह दें, और SET memory_limit = '2GB'; के साथ उसकी मेमोरी खपत को सीमित करें। अन्य सर्विसेज चलाने वाले सर्वर पर, यह सीमा ही वह चीज है जो किसी एड-हॉक क्वेरी को आपके एप्लिकेशन को RAM से बाहर करने से रोकती है।

Parquet को क्वेरी करते समय Binder Error: Referenced column "amount" not found का अर्थ लगभग हमेशा यह होता है कि फाइल का स्कीमा वह नहीं है जो आपको याद है। DESCRIBE SELECT * FROM '/srv/data/orders.parquet'; चलाएं और वास्तविक कॉलम नामों को पढ़ें।

व्यवहार में चुनाव कैसे करें

यह पूछें कि राइट पैटर्न क्या है। यदि बहुत सारे छोटे राइट्स हैं जिन्हें पावर कट के बाद भी सुरक्षित रहना चाहिए, तो SQLite का उपयोग करें। यह पूछें कि रीड पैटर्न क्या है। यदि लंबे इतिहास पर एग्रीगेट्स के साथ फुल स्कैन की आवश्यकता है, तो DuckDB का उपयोग करें। अधिकांश वास्तविक सिस्टम इन दोनों प्रश्नों का उत्तर 'हाँ' में देते हैं, और सही प्रतिक्रिया यह है कि प्रत्येक इंजन को वह कार्य दिया जाए जिसमें वह सक्षम है, न कि किसी एक को दूसरे का काम करने के लिए मजबूर किया जाए।

जिस माइग्रेशन से बचना चाहिए, वह है लाइव एप्लिकेशन स्टेट को DuckDB में ले जाना क्योंकि कोई रिपोर्ट धीमी थी। रिपोर्ट स्टोरेज लेआउट के कारण धीमी थी, इसलिए इसका समाधान डेटा एक्सपोर्ट करना है, न कि आपके राइट पाथ को फिर से लिखना।

FAQ

क्या DuckDB मेरे एप्लिकेशन डेटाबेस के लिए SQLite की जगह ले सकता है?

ऐसे डेटाबेस के लिए नहीं जिसमें बार-बार राइट (write) ऑपरेशन होते हों। DuckDB पूरी डेटाबेस फ़ाइल पर राइट लॉक लगा देता है, एक समय में केवल एक रीड-राइट प्रक्रिया की अनुमति देता है, और इसे सिंगल-रो इंसर्ट के बजाय बल्क बदलावों के लिए ट्यून किया गया है। ट्रांसेक्शनल स्टेट को SQLite में रखें और जब रिपोर्ट की आवश्यकता हो, तो DuckDB को ATTACH '/srv/app/app.db' AS app (TYPE sqlite, READ_ONLY); के साथ इसे पढ़ने दें।

क्या एनालिटिक्स के लिए DuckDB वास्तव में SQLite से तेज़ है?

बड़ी टेबल पर स्कैन और एग्रीगेट्स के लिए, हाँ, और इसका कारण स्टोरेज लेआउट है, न कि कोई ट्यूनिंग ट्रिक। DuckDB केवल उन कॉलम को पढ़ता है जिनका नाम क्वेरी में होता है और वैल्यूज़ को बैच में प्रोसेस करता है, जबकि SQLite को एक फ़ील्ड तक पहुँचने के लिए पूरी रो को स्कैन करना पड़ता है। प्राइमरी की द्वारा एक सिंगल रो को फ़ेच करने के लिए स्थिति उलट जाती है, क्योंकि SQLite दो पेज को टच करता है और DuckDB हर कॉलम के स्टोरेज को टच करता है।

क्या VPS पर DuckDB चलाने के लिए मुझे बहुत अधिक RAM की आवश्यकता है?

नहीं, लेकिन इसे एक लिमिट और डिस्क दें। :memory: के बजाय एक डेटाबेस फ़ाइल खोलें ताकि DuckDB इंटरमीडिएट परिणामों को डिस्क पर स्पिल कर सके, फिर SET memory_limit = '2GB'; को ऐसी वैल्यू पर सेट करें जिसे आपका VPS संभाल सके। बिना लिमिट के, एक बड़ी GROUP BY प्रक्रिया Out of Memory Error को ट्रिगर कर सकती है या अन्य सेवाओं को RAM से बाहर कर सकती है।

मैं अपना SQLite डेटा Parquet में कैसे लाऊं?

DuckDB से SQLite फ़ाइल को अटैच करें और COPY (SELECT * FROM app.orders) TO '/srv/data/orders.parquet' (FORMAT parquet, COMPRESSION zstd); के साथ सीधे क्वेरी को कॉपी करें। इसे बंद अवधियों के लिए शेड्यूल पर चलाएं, जैसे कि पिछले महीने की रो, और हाल की रो को SQLite में छोड़ दें जहाँ एप्लिकेशन अभी भी उन्हें लिख रहा है।

मुझे किसका बैकअप लेना चाहिए, और कैसे?

दोनों का, अलग-अलग तरीकों से। cp के बजाय sqlite3 app.db ".backup '/srv/backup/app.db'" के साथ SQLite स्नैपशॉट लें, क्योंकि एक रनिंग डेटाबेस एक -wal और एक -shm फ़ाइल भी होता है और साधारण कॉपी अधूरी हो सकती है। Parquet फ़ाइलें एक बार लिखे जाने के बाद कभी नहीं बदलतीं, इसलिए डायरेक्टरी को कॉपी करना पर्याप्त है।

#duckdb#sqlite#database#analytics#parquet#सेल्फ़-होस्टिंग