【云圖說】第235期 DDS讀寫兩步走 帶您領(lǐng)略只讀節(jié)點(diǎn)的風(fēng)采
950
2025-04-04
SQLite 是一款開源的 SQL 數(shù)據(jù)庫引擎,由于其自包含、無服務(wù)、零配置和友好的使用許可(完全免費(fèi))等特點(diǎn),在桌面和移動(dòng)平臺(tái)被廣泛使用。
在應(yīng)用開發(fā)過程中,如果想保存點(diǎn)數(shù)據(jù),自然而然地就會(huì)想到 SQLite,畢竟它擁有非常多的實(shí)踐者。這里分享一個(gè)在項(xiàng)目開發(fā)過程中遇到的 SQLite 讀寫問題——在開發(fā)一個(gè)小型桌面應(yīng)用系統(tǒng)時(shí),需求是跟蹤文件系統(tǒng)中的變更,同時(shí)對(duì)變更文件進(jìn)行相關(guān)操作,我們毫不猶豫地采用了 SQLite 來存儲(chǔ)文件變更信息。
在開發(fā)過程中,SQLite 的數(shù)據(jù)讀寫都非常順利,沒有什么障礙。然而,當(dāng)業(yè)務(wù)邏輯一切就緒開始跑業(yè)務(wù)時(shí),我們發(fā)現(xiàn)軟件處理業(yè)務(wù)的性能很差,每秒鐘只能處理 10 個(gè)左右的業(yè)務(wù)量,比數(shù)據(jù)放在內(nèi)存的老系統(tǒng)還慢得多。老系統(tǒng)也還可以達(dá)到每秒三十幾個(gè)業(yè)務(wù),而現(xiàn)在只有三分之一的水平。在有幾千幾萬個(gè)文件變更事件同時(shí)涌入的情況下,系統(tǒng)幾近停滯,會(huì)出現(xiàn)幾秒鐘一個(gè)業(yè)務(wù)的荒涼場(chǎng)景。這是不能容忍的事情。
經(jīng)過技術(shù)排查,我們發(fā)現(xiàn)對(duì) SQLite 的讀和寫都非常慢,最差的情況是從數(shù)據(jù)庫中獲取一條記錄要花掉 7 秒鐘,十分離譜。于是我們收羅學(xué)習(xí)了各種 SQLite 的優(yōu)化技術(shù)并應(yīng)用到了系統(tǒng)之中:
SQL 操作時(shí)采用事務(wù)機(jī)制
sqlite3_exec(db,"BEGIN TRANSACTION;",0,0,0); ... sqlite3_exec(db,"END TRANSACTION;",0,0,0);
批量操作時(shí),使用sqlite3_prepare而不是sqlite3_exec
sqlite3_prepare_v2(db, zSQL, -1,&stmt, &pzTail); sqlite3_step(stmt); ...
關(guān)閉數(shù)據(jù)庫的磁盤同步寫,降低數(shù)據(jù)安全性
sqlite3_exec(db,"PRAGMA synchronous = OFF; ",0,0,0);
常見的優(yōu)化技術(shù)都已使用,效果有但不太理想,還是沒有達(dá)到老系統(tǒng)的性能,更不要說超過了。
這里需要回顧一下我們的應(yīng)用模型。業(yè)務(wù)有并發(fā)處理的要求,系統(tǒng)中使用了多線程機(jī)制,這就出現(xiàn)了對(duì) SQLite 并發(fā)多讀多寫的情況。我們查閱 SQLite 的官方文檔,多寫者的情況是不適用的。
https://www.sqlite.org/whentouse.html
至此,是不是說解決的出路就只有使用 client/server 這樣的數(shù)據(jù)庫了?小應(yīng)用拖一個(gè)巨無霸數(shù)據(jù)庫,有種頭重腳輕的感覺。
記得數(shù)據(jù)庫課程的學(xué)習(xí)中,有提到大型數(shù)據(jù)庫訪問的 多層模型(N-tier),目的就是更高效地處理數(shù)據(jù)。那我們的文件型數(shù)據(jù)庫有沒有可能擁有 N-tier 的思想?盡管與大型數(shù)據(jù)庫的方法不一樣,但目的是一致的。我們分析一下現(xiàn)有應(yīng)用對(duì) SQLite 的讀寫情況,先看圖:
操作1
收到文件系統(tǒng)中的變更信息,并寫入到數(shù)據(jù)庫。由于文件變更信息是逐條發(fā)生的,無法預(yù)估事件的開始和結(jié)束,來一條寫一條的方式,導(dǎo)致開啟SQLite的事務(wù)模式也沒有啥效果。
操作2
讀取一條記錄并進(jìn)行業(yè)務(wù)操作,這里的讀取并非只讀,需要將該條記錄標(biāo)記為已選取,防止被其他業(yè)務(wù)處理線程讀取而引發(fā)重復(fù)處理。因此,這一步也存在寫操作。這里是讀一條處理一條。
操作3
業(yè)務(wù)處理完畢后,從數(shù)據(jù)庫中刪除。這里也是逐條刪除。
回顧應(yīng)用的業(yè)務(wù)操作方式后發(fā)現(xiàn),這些操作都是寫操作,而且還是逐條進(jìn)行的。問題擺在這里,技術(shù)問題還是需要通過技術(shù)來解決。在優(yōu)化的過程中,我們是分步驟進(jìn)行的——
優(yōu)化操作1
采用延遲寫的機(jī)制,收到文件變更信息后,不立即寫入數(shù)據(jù)庫,先放入緩存隊(duì)列,等到達(dá)一定時(shí)間后再進(jìn)行批量寫入,這樣在大量事件涌入時(shí)效果明顯,大大減少了數(shù)據(jù)庫的寫操作次數(shù)。
優(yōu)化操作2
使用緩存;好不容易準(zhǔn)備好數(shù)據(jù)庫查詢語句,只檢索了一條,太浪費(fèi)時(shí)機(jī),將符合檢索要求的記錄緩存起來。同時(shí)將記錄被選取的標(biāo)記放在內(nèi)存中而不寫數(shù)據(jù)庫,這樣對(duì)數(shù)據(jù)庫來說僅是讀操作。
優(yōu)化操作3
同樣采用延遲寫,將收到的刪除信息緩存起來,當(dāng)累積到一定量或者時(shí)間后,再進(jìn)行批量操作。這樣就可以充分利用 SQLite 的事務(wù)功能,大大提升寫操作的效率。
增加了這些數(shù)據(jù)庫訪問層后,數(shù)據(jù)庫的讀寫性能提升明顯,業(yè)務(wù)處理能力也達(dá)到了預(yù)期,超過了舊系統(tǒng),主要的優(yōu)化工作差不多就到此結(jié)束了。這里引入了延遲寫和緩存機(jī)制,增加了程序的復(fù)雜度,帶來的新挑戰(zhàn)是如何保持緩存記錄同數(shù)據(jù)庫記錄的一致性。為解決這個(gè)問題,使用了SQLite的自定義函數(shù):
sqlite3_create_function(...);
通過創(chuàng)建自定義函數(shù),來同步緩存記錄和數(shù)據(jù)庫記錄。比如:在從數(shù)據(jù)庫讀取業(yè)務(wù)記錄時(shí),需要排除已經(jīng)被標(biāo)為"刪除"的記錄。
經(jīng)歷這個(gè)項(xiàng)目,我們讓 SQLite 多讀多寫的并發(fā)訪問也成為了可能,算是一個(gè)收獲。(徐品華 | 天存信息)
SQLite 數(shù)據(jù)庫 軟件開發(fā) 運(yùn)維
版權(quán)聲明:本文內(nèi)容由網(wǎng)絡(luò)用戶投稿,版權(quán)歸原作者所有,本站不擁有其著作權(quán),亦不承擔(dān)相應(yīng)法律責(zé)任。如果您發(fā)現(xiàn)本站中有涉嫌抄襲或描述失實(shí)的內(nèi)容,請(qǐng)聯(lián)系我們jiasou666@gmail.com 處理,核實(shí)后本網(wǎng)站將在24小時(shí)內(nèi)刪除侵權(quán)內(nèi)容。
版權(quán)聲明:本文內(nèi)容由網(wǎng)絡(luò)用戶投稿,版權(quán)歸原作者所有,本站不擁有其著作權(quán),亦不承擔(dān)相應(yīng)法律責(zé)任。如果您發(fā)現(xiàn)本站中有涉嫌抄襲或描述失實(shí)的內(nèi)容,請(qǐng)聯(lián)系我們jiasou666@gmail.com 處理,核實(shí)后本網(wǎng)站將在24小時(shí)內(nèi)刪除侵權(quán)內(nèi)容。