DuckDB dhidi ya SQLite kwenye server: utumie zote?
SQLite huhifadhi hali ya miamala, huku DuckDB ikichanganua Parquet na CSV. Jifunze kwa nini VPS moja huendesha zote mbili, pamoja na mifano ya matumizi.
DuckDB dhidi ya SQLite kwenye server: jibu la sentensi moja
SQLite ni injini ya OLTP (uchakataji wa miamala mtandaoni): huhifadhi data kama safu mlalo na imeundwa kusoma na kuandika safu mlalo chache kwa wakati mmoja, kwa usalama na kwa kasi. DuckDB ni injini ya OLAP (uchakataji wa uchanganuzi mtandaoni): huhifadhi data kama safu wima na imeundwa kuchanganua mamilioni ya safu mlalo na kurejesha jumla moja. Zote ni maktaba zilizopachikwa, zote hufungua faili ya kawaida, na hakuna inayotumia mchakato wa server unaohitaji usimamizi wa kila mara.
Kwa hiyo, jibu la kweli kwa swali la "ipi" karibu kila mara ni "zote mbili, kwenye VPS hiyo hiyo". Programu yako huhifadhi hali yake ya sasa katika SQLite. Ripoti zako husoma faili za Parquet na CSV kwa kutumia DuckDB. Hazishindani kwa sababu hazifanyi kazi ileile.
Kwa nini hifadhi ya safu na hifadhi ya nguzo hubadilisha jibu
SQLite huandika safu kama kipande kimoja kinachofuatana ndani ya ukurasa. Kuchukua agizo moja kwa kutumia ufunguo wake msingi hugusa ukurasa mmoja wa faharasa na ukurasa mmoja wa data, yaani usomaji mara mbili. Hivi ndivyo programu hufanya maelfu ya mara kwa sekunde: soma mtumiaji huyu, sasisha kipindi hiki, ingiza agizo hili.
DuckDB huandika kila nguzo kando na kuibana. Kujumlisha amount_cents katika safu milioni tano husoma nguzo ya amount_cents pekee, huruka kila baiti nyingine katika faili, na huendesha jumla hiyo kwa msimbo wa vekta kwenye makundi ya thamani. Nguzo nyingine hazisomwi kamwe kutoka kwenye diski; hapo ndipo kasi inapotokana.
Sasa endesha kila injini kwa mzigo wa kazi wa nyingine. SQLite inapojumlisha nguzo lazima ipitie kila safu na iondoe safu nzima kutoka kwenye ukurasa ili kufikia sehemu moja, hivyo husoma data nyingi zaidi kutoka kwenye diski kuliko inavyohitaji. DuckDB inapoingiza agizo moja lazima iguse hifadhi ya kila nguzo kwa thamani moja, na huchukua kufuli la kuandika kwenye faili nzima ya hifadhidata ili kufanya hivyo. Hakuna injini iliyoharibika. Kila moja inajibu swali ambalo haikuundwa kulishughulikia.
SQLite inapong'aa: hali ya programu ya kimuamala
Chagua SQLite wakati uandishi ni mdogo, wa mara kwa mara, na haupaswi kupotea. Vipindi, maagizo, safu za foleni, mipangilio, au kitu chochote ambacho ombi la wavuti huunda.
sudo apt update && sudo apt install -y sqlite3
sudo install -d -o "$USER" -g "$USER" /srv/appUnda jedwali na uwashe uandishi wa kumbukumbu kabla ya kutekeleza katika hatua hiyo hiyo.
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);
SQLMstari wa kwanza wa matokeo ni wal. Hiyo ni PRAGMA inayoripoti hali iliyobadilishwa, na ndiyo mipangilio muhimu zaidi kwenye seva. Katika hali chaguo-msingi ya jarida la kurejesha nyuma, mwandishi huzuia kila msomaji. Katika hali ya WAL, wasomaji huendelea kusoma hali ya mwisho iliyothibitishwa huku mwandishi mmoja akiongeza data. Kwa hivyo ripoti ya polepole haicheleweshi tena ombi la wavuti linaloisubiri.
Thibitisha kuwa safu imerudishwa:
sqlite3 /srv/app/app.db "SELECT * FROM orders WHERE customer = 'ana';"Utapata 1|ana|2026-07-30T09:14:00Z|4200. Faili mbili zaidi zilitokea karibu na hifadhidata, app.db-wal na app.db-shm, na zote ni sehemu ya hifadhidata hiyo. Kunakili app.db pekee programu inapofanya kazi hukupa nakala rudufu iliyokatika. Hili limeelezwa zaidi hapa chini.
SQLite bado huruhusu mwandishi mmoja kwa wakati mmoja. Kikomo hicho ni kufuli, si foleni. Kwa hivyo mwandishi wa pili anayesubiri kwa muda mrefu sana hushindwa kwa database is locked badala ya kuzuia utekelezaji bila kikomo. Ongeza muda wa kusubiri kwa PRAGMA busy_timeout = 5000; kwenye kila muunganisho ambao programu yako hufungua. Kusubiri kwa sekunde tano huondoa makosa mengi kati ya haya katika mzigo wa kawaida wa wavuti.
Ambapo DuckDB hufaulu: uchanganuzi wa faili ulizo nazo tayari
Chagua DuckDB wakati swali linaanza na “ni ngapi”, “ni kiasi gani” au “zipi kumi bora”, na ingizo ni mkusanyiko wa faili za CSV au Parquet. Sakinisha mteja wa mstari wa amri, toleo la 1.5.5 kufikia Julai 2026:
curl https://install.duckdb.org | shHati hii husakinisha binary chini ya ~/.duckdb/cli/latest/duckdb na kuchapisha mstari wa kuiweka kwenye PATH yako. Thibitisha kuwa inaendeshwa:
~/.duckdb/cli/latest/duckdb :memory: "SELECT version();"Tengeneza faili halisi ya kuuliza. Hii huandika safu mlalo milioni tano za maagizo kwenye Parquet, zikiwa zimebanwa kwa 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);"Sasa uliza swali la uchanganuzi. Fungua shell, washa kipima muda, kisha uliza faili moja kwa moja bila hatua ya kuleta data:
.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;Soma nambari yako mwenyewe kutoka .timer badala ya kutegemea nambari iliyochapishwa, kwa sababu matokeo hutegemea diski yako na idadi ya core zako. Muundo wa mchakato huo ndio muhimu. Hakukuwa na CREATE TABLE, INSERT wala hatua ya kupakia data: DuckDB ilisoma footer ya Parquet, ikaamua ni vipande vipi vya safu vilivyohitajika na swali, kisha ikasoma vipande hivyo pekee. Saraka nzima hufanya kazi kwa njia hiyo hiyo kwa kutumia glob, FROM '/srv/data/orders-*.parquet', ambayo hubadilisha uhamishaji wa kila siku wa mwezi kuwa swali moja.
Kasi ya diski ndiyo kikomo cha msingi cha mchakato huu, na uchanganuzi wa safu ni usomaji mrefu wa mfululizo. Kwa hiyo, tofauti kati ya hifadhi ya NVMe na SATA ya zamani kwenye VPS huonekana wazi zaidi hapa kuliko inavyoonekana katika usomaji mdogo wa nasibu wa SQLite.
Kusoma hifadhidata yako ya SQLite kutoka DuckDB
Injini hizi mbili zinaunganishwa kupitia kiendelezi cha DuckDB sqlite. Ambatisha hifadhidata ya programu katika hali ya kusoma tu, ili hoja ya uchanganuzi isiweze kamwe kuandika kwenye hali ya moja kwa moja:
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;Hii husoma safu mlalo kutoka kwenye faili la SQLite wakati wa kutekeleza hoja bila kunakili data. Ni rahisi kutumia, lakini si ya haraka, kwa sababu data iliyo kwenye diski bado imehifadhiwa kwa safu mlalo na DuckDB lazima ipitie data hiyo. Itumie kwa uhamishaji, si kwa dashibodi inayopakia upya kila baada ya sekunde 30:
COPY (SELECT * FROM app.orders)
TO '/srv/data/orders-2026-07.parquet' (FORMAT parquet, COMPRESSION zstd);Kauli hiyo moja ndiyo muundo mzima. SQLite inamiliki safu mlalo mpya za moja kwa moja. Uhamishaji uliopangwa hubadilisha vipindi vilivyofungwa kuwa Parquet. DuckDB hujibu kila swali linalohusu miezi kadhaa, na hifadhidata ya programu hubaki ndogo, jambo linalowezesha uandishi wake kuwa wa haraka.
Tekeleza uhamishaji kwa ratiba badala ya kuutekeleza mwenyewe. Jozi ya huduma na kipima muda cha systemd inafaa kwa hili: unit moja inayoendesha COPY, na kipima muda kimoja kinachoitekeleza kila usiku.
Kuendesha zote mbili kwenye VPS moja
Hakuna kinachohitaji container hapa, na hakuna kinachohitaji port. Engines zote mbili ni libraries, hivyo usakinishaji unahusisha package na file path. Ikiwa sehemu nyingine ya stack yako tayari inaendeshwa chini ya Docker Compose kwenye VPS hiyo hiyo, mount data directory kwenye container inayohitaji, badala ya kuongeza database service, kwa sababu hakuna service ya kuongeza.
Kanuni mbili husaidia kuzuia matatizo katika mpangilio huu.
Ipe kila engine directory yake: /srv/app kwa SQLite file ambayo application huandika, na /srv/data kwa Parquet files ambazo analytics husoma. Zinapotumia directory moja, backup job inayopiga snapshot ya moja huishia kugongana na nyingine.
Usielekeze processes mbili kwenye DuckDB database file moja katika read-write mode. Process moja pekee inaweza kushikilia DuckDB file kwa ajili ya kuandika, na ya pili hushindwa kuifungua kabisa. Readers wengi wanaruhusiwa ikiwa kila mmoja ataweka access_mode = 'READ_ONLY'. Hili huwashangaza watu wanaotoka SQLite, ambako processes kadhaa hushiriki file mara kwa mara. Ikiwa analytics yako inasoma Parquet files pekee, suala hili halitokei, na hiyo ni sababu nyingine ya kuweka durable state katika SQLite.
Nakala hutofautiana, na tofauti hiyo husababisha matatizo
Hifadhidata ya SQLite inayotumika ina faili tatu. Kuzinakili kwa kutumia cp katikati ya uandishi hukupa faili inayofunguka lakini iliyo na data isiyo sahihi. Tumia amri ya nakala rudufu ya engine yenyewe. Amri hiyo huchukua picha thabiti huku programu ikiendelea kuandika:
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 huchapisha ok kwenye nakala nzuri. Matokeo mengine yoyote yanamaanisha uifute picha hiyo na uchukue nyingine.
Faili za Parquet hazibadiliki baada ya kuandikwa. Kwa hiyo hazihitaji utunzaji maalum: hifadhi nakala ya saraka. Tuma njia zote mbili nje ya server kwa kutumia nakala rudufu za restic kutoka VPS yako. Kwa njia hiyo, safu nzima ya data huwa saraka mbili katika kazi moja ya kuhifadhi nakala.
Aina za hitilafu na mifuatano halisi utakayoona
Error: database is locked kutoka SQLite humaanisha kuwa muunganisho mwingine uliishikilia kufuli la kuandika kwa muda mrefu kuliko muda wako wa kusubiri ulivyoruhusu. Si uharibifu wa data. Weka PRAGMA busy_timeout kwenye kila muunganisho, kisha tafuta muamala mrefu ambao ulipaswa kuwa miamala kadhaa mifupi.
Error: unable to open database file baada ya kubadilisha ruhusa kwa kawaida humaanisha kuwa mchakato unaweza kuandika kwenye faili, lakini hauwezi kuandika kwenye saraka yake. SQLite huunda app.db-wal na app.db-shm kando ya hifadhidata, kwa hiyo saraka yenyewe lazima iwe na ruhusa ya kuandikwa, si faili ya .db pekee.
IO Error: Could not set lock on file kutoka DuckDB humaanisha kuwa mchakato wa pili tayari umefungua hifadhidata hiyo kwa ajili ya kuandika. Funga shell nyingine, au fungua yako katika hali ya kusoma pekee.
Out of Memory Error kutoka DuckDB kwenye VPS ndogo humaanisha kuwa query ilihitaji kumbukumbu ya kufanya kazi zaidi kuliko iliyokuwa inapatikana. DuckDB hutumia diski kuhifadhi data ya muda inapowezekana, kwa hiyo ipe mahali pa kuhifadhi data hiyo kwa kufungua faili ya hifadhidata kwenye diski badala ya :memory:, na punguza matumizi yake kwa SET memory_limit = '2GB';. Kwenye mashine inayoendesha huduma nyingine, kikomo hicho ndicho huzuia query ya muda kumaliza RAM ya programu yako.
Binder Error: Referenced column "amount" not found wakati wa kuhoji Parquet karibu kila mara humaanisha kuwa schema ya faili si ile unayoikumbuka. Tekeleza DESCRIBE SELECT * FROM '/srv/data/orders.parquet'; na usome majina halisi ya safu yanayorejeshwa.
Jinsi ya kuchagua kwa matumizi halisi
Uliza muundo wa uandishi ni upi. Uandishi mwingi mdogo unaopaswa kuendelea baada ya kukatika kwa umeme unamaanisha utumie SQLite. Uliza muundo wa usomaji ni upi. Uchanganuzi kamili wenye hesabu za jumla kwenye historia ndefu unamaanisha utumie DuckDB. Mifumo mingi halisi hujibu ndiyo kwa maswali yote mawili. Jibu sahihi ni kuupa kila injini sehemu inayofaa, badala ya kuilazimisha moja ichukue jukumu la nyingine.
Uhamishaji unaopaswa kuepukwa ni kuhamisha hali hai ya programu kwenye DuckDB kwa sababu ripoti ilikuwa polepole. Ripoti ilikuwa polepole kwa sababu ya mpangilio wa hifadhi. Kwa hiyo, suluhisho ni kusafirisha data, si kuandika upya njia yako ya uandishi.
FAQ
Je, DuckDB inaweza kuchukua nafasi ya SQLite kwa hifadhidata ya programu yangu?
Si kwa hifadhidata inayoandikwa mara kwa mara. DuckDB huweka write lock kwenye faili zima la hifadhidata, huruhusu mchakato mmoja tu wa kusoma na kuandika kwa wakati mmoja, na imeundwa kwa mabadiliko ya jumla badala ya kuingiza safu moja moja. Hifadhi hali ya miamala katika SQLite na iruhusu DuckDB kuisoma kwa ATTACH '/srv/app/app.db' AS app (TYPE sqlite, READ_ONLY); wakati ripoti inaihitaji.
Je, DuckDB ni ya haraka kweli kuliko SQLite kwa uchanganuzi?
Kwa uchanganuzi wa data na jumla za thamani kwenye jedwali kubwa, ndiyo. Sababu ni mpangilio wa uhifadhi, si ujanja wa tuning. DuckDB husoma safu wima zilizotajwa na query pekee na huchakata thamani kwa makundi, huku SQLite ikilazimika kupitia safu nzima ili kufikia sehemu moja. Kwa kupata safu moja kwa primary key, mpangilio hubadilika, kwa sababu SQLite hugusa pages mbili, huku DuckDB ikigusa uhifadhi wa kila safu wima.
Je, ninahitaji RAM nyingi ili kuendesha DuckDB kwenye VPS?
Hapana, lakini ipe kikomo na diski. Fungua faili la hifadhidata badala ya :memory: ili DuckDB iweze kuhamishia matokeo ya muda kwenye diski, kisha weka SET memory_limit = '2GB'; kwenye thamani ambayo VPS yako inaweza kutenga. Bila kikomo, GROUP BY kubwa inaweza kuongeza Out of Memory Error au kusababisha huduma nyingine ziondolewe kwenye RAM.
Ninawezaje kuhamisha data yangu ya SQLite hadi Parquet?
Ambatisha faili la SQLite kutoka DuckDB na unakili query moja kwa moja kwa COPY (SELECT * FROM app.orders) TO '/srv/data/orders.parquet' (FORMAT parquet, COMPRESSION zstd);. Iendeshe kwa ratiba kwa vipindi vilivyofungwa, kama vile safu za mwezi uliopita, na uache safu za hivi karibuni katika SQLite ambako programu bado inaziandika.
Ni ipi ninayopaswa kuhifadhi nakala, na kwa njia gani?
Zote mbili, kwa njia tofauti. Tengeneza snapshots za SQLite kwa sqlite3 app.db ".backup '/srv/backup/app.db'" badala ya cp, kwa sababu hifadhidata inayoendeshwa pia ni -wal na faili la -shm, na nakala ya kawaida inaweza kukatika. Faili za Parquet hazibadiliki baada ya kuandikwa, kwa hiyo kunakili directory kunatosha.