कंप्यूटरों के बीच SQLite डेटाबेस को तेज़ी से कॉपी करने का तरीका
(alexwlchan.net)- बड़े SQLite डेटाबेस को remote server से local में ले जाते समय मूल
.dbफ़ाइल की जगह gzip-compressed SQL dump कॉपी करने से ट्रांसफ़र डेटा काफी कम हो जाता है - फ़ाइल के बड़े होने की मुख्य वजह index होते हैं, जो query को तेज़ करने के लिए table data को दोहराकर store करते हैं और disk usage बढ़ाते हैं
- SQLite का
.dumpडेटाबेस को SQL statements के text में बदल देता है, और index को वास्तविक data की जगहCREATE INDEXकी एक लाइन के रूप में दिखाता है, जिससे duplicate transfer से बचा जा सकता है - उदाहरण डेटाबेस में मूल फ़ाइल 3.4GB, dump text 1.3GB, और gzip-compressed फ़ाइल 240MB थी; compressed dump मूल फ़ाइल से 14 गुना छोटा था
- कॉपी करने से पहले dump फ़ाइल को स्थिर रूप में बना लेने पर ट्रांसफ़र के दौरान मूल फ़ाइल बदलने से होने वाली
database disk image is malformedत्रुटि की संभावना कम हो जाती है
SQLite dump को compress करके कॉपी करना
- छोटे SQLite डेटाबेस को
rsyncसे सीधे कॉपी करना पर्याप्त हो सकता है- उदाहरण:
rsync --progress username@server:my_remote_database.db my_local_database.db
- उदाहरण:
- डेटाबेस बड़ा होने पर direct copy धीमी और अस्थिर हो सकती है
- home internet पर 250MB डेटाबेस को web server से डाउनलोड करने में लगभग 1 मिनट लगता है
- कई GB आकार के डेटाबेस में इंतज़ार और बढ़ जाता है
- आकार बढ़ने की सबसे बड़ी वजह index हैं
- index query speed को बहुत बढ़ाते हैं, लेकिन डेटाबेस फ़ाइल का आकार भी बढ़ाते हैं
- एक डेटाबेस में एक खास index अकेले disk size का आधा हिस्सा ले रहा था
- index अनोखा data रखने से ज़्यादा दूसरे tables के data को दोहराकर lookup को तेज़ बनाते हैं
- SQLite पूरे डेटाबेस को text file के रूप में dump कर सकता है
- कमांड:
sqlite3 my_database.db .dump > my_database.db.txt - बनी हुई फ़ाइल
CREATE TABLE,INSERT INTO,CREATE INDEXजैसी SQL statements से बनी होती है - index को वास्तविक index data की जगह
CREATE INDEX [idx_photo_locations] ON [photos] ([longitude], [latitude]);जैसे creation command के रूप में store किया जाता है
- कमांड:
- dump फ़ाइल को फिर से SQLite डेटाबेस में restore किया जा सकता है
- कमांड:
cat my_database.db.txt | sqlite3 my_reconstructed_database.db
- कमांड:
- SQL dump में दोहराव ज़्यादा होता है, इसलिए gzip compression बहुत प्रभावी रहता है
- उदाहरण:
sqlite3 explorer.db .dump | gzip -c > explorer.db.txt.gz - आकार तुलना:
- मूल SQLite डेटाबेस: 3.4GB
- text dump: 1.3GB
- gzip-compressed text: 240MB
- gzip-compressed फ़ाइल मूल SQLite डेटाबेस से 14 गुना छोटी थी
- उदाहरण:
वास्तविक प्रक्रिया और reliability का लाभ
- नया तरीका यह है कि remote server पर compressed dump बनाया जाए, सिर्फ वही फ़ाइल डाउनलोड की जाए, और फिर local पर डेटाबेस को दोबारा बनाया जाए
- कॉपी करने की प्रक्रिया इस प्रकार है
- server पर gzip-compressed text फ़ाइल बनाना:
ssh username@server "sqlite3 my_remote_database.db .dump | gzip -c > my_remote_database.db.txt.gz" - local में कॉपी करना:
rsync --progress username@server:my_remote_database.db.txt.gz my_local_database.db.txt.gz - server की अस्थायी compressed फ़ाइल हटाना:
ssh username@server "rm my_remote_database.db.txt.gz" - local पर decompress करना:
gunzip my_local_database.db.txt.gz - local डेटाबेस को फिर से बनाना:
cat my_local_database.db.txt | sqlite3 my_local_database.db - local text फ़ाइल हटाना:
rm my_local_database.db.txt
- server पर gzip-compressed text फ़ाइल बनाना:
- compressed dump एक consistent copy source की तरह काम करता है
- बड़े डेटाबेस को लंबे समय तक कॉपी करते हुए अगर मूल फ़ाइल अपडेट हो जाए, तो नतीजे वाली फ़ाइल का शुरुआती हिस्सा update से पहले का और बाद का हिस्सा update के बाद का हो सकता है
- ऐसी फ़ाइल local में खोलने पर
database disk image is malformedत्रुटि आ सकती है
- कॉपी से पहले text dump बना लेने पर
rsyncजिस फ़ाइल को भेज रहा है, वह बीच में नहीं बदलती- इससे पूरी और consistent text फ़ाइल कॉपी होने की संभावना बढ़ जाती है
- अगर आप बड़े SQLite डेटाबेस को बार-बार स्थानांतरित करते हैं, तो इस तरीके से डाउनलोड समय घटाया जा सकता है और कॉपी की reliability बढ़ाई जा सकती है
1 टिप्पणियां
Hacker News की राय
चल रही और अपडेट्स पा रही database file को वैसे ही copy करेंगे तो जाहिर है वह corrupt हो सकती है
SQLite database को सुरक्षित रूप से replicate करने के लिए Litestream है: https://github.com/benbjohnson/litestream
पसंदीदा service तो है, लेकिन कोई भी SFTP server चलेगा
[1] https://github.com/benbjohnson/litestream/issues/140
[2] https://www.rsync.net/resources/notes/2021-q3-rsync.net_technotes.html
बेशक अगर कई files हों, तो उन्हें subvolume में बांधना होगा ताकि सब एक ही transaction में copy हों, और सिर्फ
cp --reflink=alwaysकाफी नहीं हैSIGSTOPसे process रोकना भी मिलता-जुलता परिणाम दे सकता है, लेकिन मैं उस पर निर्भर नहीं रहना चाहूंगा.backupcommand भी एक official tool है, जिसका मकसद running database का snapshot version बनाकर उसे copy करने देना हैअगर मुझे ठीक याद है तो उसने एक बेहतरीन guide लिखी थी, लेकिन उसे ढूंढ पाने की संभावना लगभग 0 है। अगर किसी को यह reference पता हो तो link डाल दे, अच्छा होगा
computers के बीच database copy करने की बात है, लेकिन ऐसा लगता है जैसे सिर्फ circle बना दिया और बाकी उल्लू छोड़ दिया
जैसा दूसरों ने कहा है, incremental
rsyncकहीं तेज होगा, लेकिन ज्यादा खटकने वाली बात यह है कि SQL statements भेजना database भेजने से तेज है, ऐसा कहते हुए उस SQL statements को execute करना पड़ेगा वाली बात पूरी तरह छोड़ दी गई है। फिर/optimize/भी चलाना होगा और/vacuum/भीअभी मेरे पास एक ऐसी स्थिति है जहां CSV files से database को “incrementally rebuild” करना पड़ता है, और मेरे मामले में शुरू से फिर बनाना ज्यादा optimal है। फिर भी काफी optimize करने के बावजूद खाली in-memory database में batch insert करने और indexes बनाने में ही 30 मिनट लगते हैं
SQLite3 में तेजी से insert करने के तरीकों का बहुत अच्छा सारांश है
कहा गया था कि 250MB database download करने में 1 मिनट लगता है, जबकि मैंने 1Gbps fiber connection की वजह से company server से 2GB का SQLite test database 15 सेकंड में खींच लिया
Bloom filter और LRU cache जैसी तरकीबें लगानी पड़ीं, लेकिन अब 6GB database करीब 20 मिनट में बना सकता हूं
SQLite में इस काम के लिए official tool है: https://www.sqlite.org/rsync.html
यह page level पर काम करता है
“protocol में replica हर page का cryptographic hash original की तरफ भेजता है, और original उन pages की पूरी content वापस भेजता है जिनके hash match नहीं करते”
यह tool इस्तेमाल करना है तो खुद compile करने के अलावा कोई रास्ता नहीं है
हाल में release हुई
sqlite_rsyncutility SQLite database की internal structure के हिसाब से optimized rsync algorithm version इस्तेमाल करती है। यह internal data pages को efficiently compare करती है और फिर सिर्फ बदले हुए या missing pages को sync करती हैलेख की tips भी अच्छी हैं, लेकिन अब built-in utility इस्तेमाल करना ज्यादा आसान है :)
मैंने इसके काम करने का तरीका blog में विस्तार से लिखा है: https://nochlin.com/blog/how-the-new-sqlite3_rsync-utility-works
sqlite3_rsyncअब rsync.net platform में built-in हैssh user@rsync.net sqlite3_rsync … blah blah …इसे पिछले हफ्ते ही add किया गया है, इसलिए अभी सभी regions में deploy नहीं हुआ है, लेकिन शुरुआती users ने कहा कि यह ठीक वैसा ही चला जैसा उम्मीद थी
sqlite_rsyncसिर्फ WAL mode में इस्तेमाल किया जा सकता है। WAL mode की एक अतिरिक्त constraint यह है कि database file local disk पर होनी चाहिएज्यादातर मामलों में यह स्वाभाविक रूप से ऐसा ही होगा, लेकिन जहां यह संभव नहीं है, वहां यह utility इस्तेमाल नहीं कर सकते
यह थोड़ा आश्चर्यजनक था कि
rsyncमें मिलने वाली ट्रांसफर के दौरान compression की कोशिश नहीं की गई-z, --compressट्रांसफर के दौरान file data को compress करता है, और--compress-level=NUMcompression level को स्पष्ट रूप से तय करता हैशायद gzip से compress करके फिर भेजना तेज हो सकता है, लेकिन सिर्फ एक flag से transfer बेहतर किया जा सकता है, यह अच्छी बात है
-zके साथsqlite3_rsync(https://sqlite.org/rsync.html) इस्तेमाल करना बेहतर हैयानी SQLite transactions और WAL को समझने वाले rsync में transfer-time compression भी जुड़ जाता है
ऐसे काम करते समय dump को सीधे gzip में stream करता हूं। आम तौर पर intermediate file के बिना सीधे destination तक stream करने का तरीका भी मिल जाता है
ऊपर से ऐसा करने पर destination पर compressed form में save होता है। अगर मकसद गरीब आदमी की replication नहीं, बल्कि backup है, तो खासकर यही सही है
rsyncइस्तेमाल करने सेrsyncका मूल फायदा, यानी rolling checksum based delta transfer, पूरी तरह bypass हो जाता है--remove-source-filesइस्तेमाल किया होता तो दोबारा SSH में जाकरrmकरने की जरूरत नहीं पड़तीtext file के रूप में save करना inefficient है। मैं SQLite database को
VACUUM INTOसे save करता हूंsqlite3 -readonly /path/db.sqlite "VACUUM INTO '/path/backup.sqlite';"https://sqlite.org/lang_vacuum.html के अनुसार,
INTOclause वालाVACUUMcommand running database की backup copy बनाने के लिए backup API का alternative है। फायदा यह है कि result backup database का size minimum हो जाता है, जिससे filesystem I/O कम हो सकता हैVACUUM INTOapproach indexes को बनाए रखता हैtext file अपने-आप में inefficient हो सकती है, लेकिन gzip जैसे basic tools से भी अच्छी तरह compress हो जाती है। SQLite binary format भी उतना ही अच्छी तरह compress होता है या नहीं, यह पक्का नहीं है, पर संभव है
DuckDB में यही काम करते हुए Parquet में export किया जा सकता है, जिससे data size text-based SQL statements की तुलना में single-digit multiplier तक छोटा हो जाता है। transfer भी तेज होता है और load भी तेज होता है
https://duckdb.org/docs/stable/sql/statements/export.html
duckdb -c "attach 'sqlite-database.db' as db; copy db.table_name to 'table_name.parquet' (format parquet, compression zstd)"मेरे test database में यह gzip से compressed text SQL statements की तुलना में लगभग 20% छोटा था
SQLite में एक session extension है जो tables के set में changes को track करता है और पुराने version के SQLite database पर apply किए जा सकने वाले changeset/patchset बनाता है
https://www.sqlite.org/sessionintro.html
अपने एक project में मैंने गरीब आदमी का session खुद implement किया था: सभी SQL statements और parameters को अलग database में record किया, फिर उसे sync करके replay किया। करीब 30GB के database के लिए, जिसमें रोज लगभग 0.1% ही बदलता है, यह काफी अच्छी तरह काम करता है
अगर पुराने version से नए version में नियमित रूप से sync करते हैं, तो gzip के
--rsyncableoption से और optimize किया जा सकता हैcompression ratio लगभग 1% कम हो जाता है, लेकिन एक version से अगले version तक का difference compressed output के पूरे हिस्से में chain नहीं होता, बल्कि localized रहता है
एक और तरीका यह है कि dump output को compress न करें,
rsyncको पुराने uncompressed dump और current dump का diff calculate करने दें, और फिर network पर भेजे जाने वाले changeset को ही compress करे। यानीrsync -zइस्तेमाल करेंलगता है इसे एक pipeline के रूप में handle किया जा सकता है
ssh username@server "sqlite3 my_remote_database.db .dump | gzip -c" | gunzip -c | sqlite3 my_local_database.db-oCompression=onया-Cसे SSH compression on करते हैं, तोgzip/gunzipशायद duplicate हो सकता हैpvभी होना चाहिए