文由微信客戶端技術團隊工程師“Jon”分享,原題“Windows微信:消息數據庫架構演進”,有較多修訂。
本文分享的是,微信客戶端團隊基于對微信用戶日常使用場景和數據分析,通過分離重要和非重要數據、采用可靠的分庫策略等,對微信Windows端IM本地數據庫的架構進行的優化和改造,并最終得到一個具備良好實踐效果的技術改造方案。
微信的Windows客戶端自2014年上線以來,用戶數穩步增長。隨著時間的不斷推移,很多用戶本地積攢的消息量越來越大。
最初的本地IM數據庫設計秉著遵循“簡單易用、方便管理”的原則,把用戶收到的所有消息都統一存放在用戶當前客戶端本地的“同一個SQLite數據文件中”。
(作者注:微信不會保存聊天記錄,聊天內容只存儲在用戶手機、電腦等終端設備上。)
由于初期這套本地數據庫設計方案的短板,隨著目前微信使用越來越廣泛、消息堆積越來越多,從而逐漸暴露出了許多技術問題。
隨著使用時間的推移,數據也逐漸增多,當數據量越來越龐大:
從文件系統的角度,數據庫文件是逐頁增長的。因為長時間的使用微信會使得消息量的逐步累積,讓數據庫體積逐漸增長,也會導致碎片化更嚴重,這在機械硬盤下,也會進一步影響讀寫效率。
對用戶最直觀的影響就是——切換聊天變得很卡,這個問題對于重度用戶尤甚,甚至會出現點擊聊天就卡頓的情況。
隨著時間的推移,消息量的逐步累積,數據庫存儲文件的體積也是越來越大,顯著占用用戶存儲空間。
磁盤文件意外損壞也有可能導致數據丟失。
因為所有消息都放到一個數據庫文件,就類似把所有雞蛋放在一個籃子。
數據庫文件也可能會因為存儲壞道、電腦意外斷電、sqlite自身bug等原因導致數據庫文件發生損壞。如果發生損壞時,有可能導致用戶丟失消息數據。即使有DB恢復機制,也無法保證能恢復出所有歷史記錄。
當這種情況發生時,對用戶影響十分大,因為聊天記錄可能沒了!
PS:微信移動端也有類似困擾,有興趣可以閱讀《微信客戶端SQLite數據庫損壞修復實踐》。
上述數據庫存儲文件變大和查詢變慢的問題,都是由于消息數據的不斷增多引起。
但消息數的增長是無法避免的,那么有沒有辦法控制增長速度,并且控制數據庫的大小?
我們從兩個方向進行分析:消息情況、日常使用場景
微信里的IM消息可分為三大類:
按消息的重要性來說:
按消息的大小來說:
眾所周知,我們日常使用微信,都是收發消息,或者瀏覽最近的消息。對于更早的消息,我們一般很少會主動去瀏覽。
越早的消息,瀏覽的概率越低。
所以:在大多數場景下,我們要讓最常訪問的消息,不受老數據的影響。
針對前述問題并結合上述分析,我們從以下方面對微信Windows端本地SQLite數據庫的架構進行了演進和優化。
涉及的主要優化內容和手段有:
下面我們將逐一詳細介紹。
基于以上分析,首先把公眾號消息劃分出去,存到單獨的一個數據庫,跟用戶的普通消息隔離,同時也可以大幅減少普通消息數據庫的體積。
基于日常使用場景的分析,大部分老數據讀取的頻率很低,所以應該提高最近一段時間的讀寫效率。
對于上述這種情況,我們采取了以時間和空間動態劃分數據庫的方案。初始默認值是每個數據庫存放半年的消息,超過時間之后新建一個數據庫存放。對于大部分使用場景,我們只需要讀寫最新的數據庫就可以滿足需求,如果需要瀏覽更早的消息,可以再打開之前的數據庫進行讀取。
除了時間維度,我們還考慮了空間維度的劃分:如果半年內消息普通消息規模超過閾值,也會新建一個數據庫進行存儲,讓每個數據庫大小和數據規模不至于太大,能提升最近一段時間消息的讀寫效率。
對于最廣泛的使用場景——查看每一個聊天的消息,這種場景需要對每一個聊天會話建立一個索引。
這里的索引方案我們參考了安卓端:即將每一個聊天轉換成一個數值型的ID,從而減少每條索引的長度,提高索引的讀寫效率。(關于微信的移動端SQLite完整數據庫結構,可以參考:《微信本地數據庫破解版(含iOS、Android),僅供學習研究 [附件下載]》)
除此之外,我們還對一些經常訪問的內容,單獨提取成為一個字段,并且增加索引。比如消息的子類型(這個在老數據庫中是一個序列化字段),它沒有索引,但這個字段經常需要用到,所以單獨提出成為一列,并且加上索引,為消息按類型查找提供方便。
IM中消息顯然總是會越來越多的,但如何能夠在不影響讀寫效率的同時,減少/壓縮消息數據的體積,也是我們的優化方向。
從上面的數據看,部分消息體積較大,已經超過了數據庫每頁的大小(Page Size)。
數據庫是按頁存儲數據的,Page Size是數據庫一頁能夠容納的數據。如果一條數據,一個頁放不下,就需要用到溢出頁,把多出來放不下的數據放到溢出頁中,溢出頁可以有多個。
這時候,如果讀取這條數據,就需要把溢出頁也全部讀出來,會增加IO的消耗。
如果壓縮數據,能夠把消息體壓縮到一個頁能放得下,減少溢出頁的使用,是可以增加IO性能的。
SQLite數據庫溢出頁結構:
(上圖引用自書籍《The Definitive Guide to SQLite》第308頁)
PS:《The Definitive Guide to SQLite》這本書的電子版我也給你找到了,請從下面附件處下載:
The Definitive Guide to SQLite (2nd edition, 2010)-52im.net.pdf.zip (3.61 MB)
但是壓縮需要占用CPU資源,這里選擇一種能夠平衡性能和壓縮率的算法是關鍵。
經過對比壓縮算法的Benchmark,并且對消息體壓縮性進行實測,最終選擇了一個高性能壓縮算法:lz4。
經過對測試帳號的數據分析,不同類型的消息體大小差異較大。
一般來說:文本消息的長度不會特別大,但是網頁卡片類型的消息,體積會較大。由于不同的消息長度,獲得的壓縮率不一樣,太短的文本長度,壓縮起來并沒有意義。
所以經過消息體長度、壓縮、,壓縮性能的分析,最終確定對網頁卡片等進行壓縮,在較低性能消耗的前提下,綜合壓縮率可達到40%,減少了IO次數 。
如果數據庫文件由于外部原因發生損壞,則會對體驗造成較大影響。降低損壞率和減少損壞帶來的數據損失,也是我們改進的方向。
按照時間維度劃分數據庫之后,相當于把消息按時間分散存儲。最新的數據庫負責讀寫最近的消息,其余的數據庫只需要根據需求支持瀏覽查看消息。
對于老數據庫而言:可以做到按需加載,從而減少了對數據庫的讀寫,也減少了這些數據庫損壞的幾率。一旦有數據庫出現損壞,即使無法恢復,也不會所有消息全部丟失,只會丟失該數據庫對應時間段的消息,這也可以減少部分數據庫損壞帶來的損失。
在早期使用的單數據庫架構中,由于數據會越攢越多,數據庫體積會持續變大,很難去做備份。分庫之后,每個數據庫體積變小,因而數據庫備份變得更為可行。因為最新的數據庫存在頻繁的消息讀寫,發生損壞的概率遠高于老數據庫,所以這里對最新的一個數據庫做定期的備份。
默認配置下,我們每間隔一段時間會對最新的數據庫進行一次備份,該備份是最新的一個數據庫的完整拷貝。若最新的數據庫在讀寫時發生損壞,會先嘗試從備份數據恢復。若恢復成功,則最多丟失從備份到恢復這段時間的數據,進一步降低損壞造成的損失。
經過對比,對于一個在測試帳號中原始的消息數據庫,壓縮后大小可以減少接近一半,同時溢出頁數和需要使用溢出頁的記錄數減少也超過一半。
對于讀寫性能,對比壓縮前,壓縮后的讀取和解壓縮性能比之前有接近10%的提升。
后續我們微信客戶端團隊將繼續研究數據庫修復相關的實踐,持續關注數據庫相關的性能數據,提升可靠性,打造更好的用戶體驗!
以下是相關技術文章,有興趣的讀者可以一并閱讀:
學習交流:
- 移動端IM開發入門文章:《新手入門一篇就夠:從零開發移動端IM》
- 開源IM框架源碼:https://github.com/JackJiang2011/MobileIMSDK(備用地址點此)
(本文已同步發布于:http://www.52im.net/thread-4034-1-1.html)
信掃碼 一鍵預約上門修電腦
電腦卡頓和慢速可能有多種原因,以下是一些常見原因和相應的解決方法:
在嘗試解決問題之前,建議先備份重要數據。如果問題較為復雜或您不確定如何處理,最好請專業技術人員協助。他們可以進行更深入的故障排除或提供適合您情況的解決方案。
T之家 5 月 31 日消息,據網友反饋,微信 Windows PC 電腦版 3.7.0.26 正式發布,可以在程序里面的關于微信-檢查更新里面更新了。該版本帶來了一些新功能和改版、修復等內容,包括可以對存儲空間進行管理,在群公告中可添加圖片、視頻、文件等內容,圖片視頻瀏覽界面全新改版等等。
Windows 微信 3.7.0 新增功能:
-可以對存儲空間進行管理;
-在群公告中可添加圖片、視頻、文件等內容;
-圖片視頻瀏覽界面全新改版;
-修復了一些已知問題。
大家在IT之家微信號回復“微信”兩字,即可獲取當前最新官方內部版微信下載。