Windows微信更新了什么內(nèi)容(分析Windows微信的優(yōu)勢(shì))
本文基于微信用戶日常使用場(chǎng)景 & 數(shù)據(jù)分析, 「 通過(guò)分離重要 / 非重要數(shù)據(jù)、采用可靠的分庫(kù)策略等 」 ,對(duì)微信數(shù)據(jù)庫(kù)架構(gòu)進(jìn)行優(yōu)化 & 改造,并最終得到一個(gè)具備實(shí)踐良好效果的改造方案。
背景說(shuō)明
微信 for Windows自2023年上線以來(lái),用戶數(shù)穩(wěn)步增長(zhǎng)。隨著時(shí)間的不斷推移,用戶積攢的消息量越來(lái)越大。最初的數(shù)據(jù)庫(kù)設(shè)計(jì)秉著 「 遵循簡(jiǎn)單易用,方便管理 」 的原則,把用戶收到的所有消息都統(tǒng)一存放在用戶當(dāng)前客戶端本地的 「 同一個(gè)數(shù)據(jù)文件中。 」
(注:微信不會(huì)保存聊天記錄,聊天內(nèi)容只存儲(chǔ)在用戶手機(jī)、電腦等終端設(shè)備上。)
目前問(wèn)題
該方案隨著目前微信使用越來(lái)越廣泛、消息越來(lái)越多而逐漸暴露出許多問(wèn)題:
問(wèn)題1:慢
隨著使用時(shí)間的推移,數(shù)據(jù)也逐漸增多,數(shù)據(jù)庫(kù)的查詢和插入效率會(huì)受到影響;即使消息數(shù)據(jù)庫(kù)存在索引,當(dāng)數(shù)據(jù)量越來(lái)越龐大,索引的查詢效率也隨之下降。
從文件系統(tǒng)的角度,數(shù)據(jù)庫(kù)文件是逐頁(yè)增長(zhǎng)的。因?yàn)殚L(zhǎng)時(shí)間的使用微信會(huì)使得消息量的逐步累積,讓數(shù)據(jù)庫(kù)體積逐漸增長(zhǎng),也會(huì)導(dǎo)致碎片化更嚴(yán)重,這在機(jī)械硬盤(pán)下,也會(huì)進(jìn)一步影響讀寫(xiě)效率。
對(duì)用戶最直觀的影響就是: 「 切換聊天變得很卡,這個(gè)問(wèn)題對(duì)于重度用戶尤甚,甚至?xí)霈F(xiàn)點(diǎn)擊聊天就卡頓的情況。 」
問(wèn)題2:大
隨著時(shí)間的推移,消息量的逐步累積,數(shù)據(jù)庫(kù)體積也是越來(lái)越大,占用用戶存儲(chǔ)空間。
問(wèn)題3:磁盤(pán)文件損壞
磁盤(pán)文件意外損壞也有可能導(dǎo)致數(shù)據(jù)丟失。因?yàn)樗邢⒍挤诺揭粋€(gè)數(shù)據(jù)庫(kù)文件,就類(lèi)似把所有雞蛋放在一個(gè)籃子。數(shù)據(jù)庫(kù)文件也可能會(huì)因?yàn)榇鎯?chǔ)壞道、電腦意外斷電、sqlite自身bug等原因?qū)е聰?shù)據(jù)庫(kù)文件發(fā)生損壞。如果發(fā)生損壞時(shí),有可能導(dǎo)致用戶丟失消息數(shù)據(jù)。即使有DB恢復(fù)機(jī)制,也無(wú)法保證能恢復(fù)出所有歷史記錄。
當(dāng)這種情況發(fā)生時(shí),對(duì)用戶影響十分大, 「 因?yàn)榱奶煊涗浛赡軟](méi)了! 」
原因分析
上述變大和變慢的問(wèn)題, 「 都是由于消息數(shù)據(jù)的不斷增多引起。 」 但消息數(shù)的增長(zhǎng)是無(wú)法避免的, 「 那么有沒(méi)有辦法控制增長(zhǎng)速度,并且控制數(shù)據(jù)庫(kù)的大小? 」
我們從兩個(gè)方向進(jìn)行分析:消息情況、日常使用場(chǎng)景
分析1:消息情況
消息分類(lèi)
用戶消息可分為三大類(lèi):單人聊天,群聊,以及訂閱號(hào)/服務(wù)號(hào)消息(統(tǒng)稱為公眾號(hào)消息)。
從重要性區(qū)分:
單聊和群聊消息:用戶的私人消息,被刪除或者丟失無(wú)法恢復(fù),對(duì)用戶損失最大;
公眾號(hào)消息:因?yàn)橹灰P(guān)注了公眾號(hào),都可以拉取閱讀,屬于公共的消息,所以對(duì)用戶來(lái)說(shuō)重要性稍低。
消息大小
基于對(duì)測(cè)試帳號(hào)的消息大小數(shù)據(jù)分析,我們發(fā)現(xiàn),占總條數(shù)比例不高的公眾號(hào)消息,占用了超過(guò)一半的數(shù)據(jù)庫(kù)空間。
經(jīng)過(guò)對(duì)測(cè)試帳號(hào)消息類(lèi)型的分析,網(wǎng)頁(yè)卡片類(lèi)消息是公眾號(hào)消息的主要類(lèi)型,其平均消息體大小是文本消息的幾十倍。
分析2:日常應(yīng)用場(chǎng)景分析
眾所周知,我們?nèi)粘J褂梦⑿?都是收發(fā)消息,或者瀏覽最近的消息。對(duì)于更早的消息,我們一般很少會(huì)主動(dòng)去瀏覽。越早的消息,瀏覽的概率越低,所以在大多數(shù)場(chǎng)景下,我們要讓最常訪問(wèn)的消息,不受老數(shù)據(jù)的影響。
解決方案
針對(duì)上述問(wèn)題 & 結(jié)合分析,從以下方面對(duì)微信數(shù)據(jù)庫(kù)的架構(gòu)進(jìn)行演進(jìn) & 優(yōu)化 :
分庫(kù)改造
建立消息索引
消息體積優(yōu)化
提高數(shù)據(jù)庫(kù)健壯性
1. 分庫(kù)改造
基于以上分析,首先把公眾號(hào)消息劃分出去,存到單獨(dú)的一個(gè)數(shù)據(jù)庫(kù),跟用戶的普通消息隔離,同時(shí)也可以大幅減少普通消息數(shù)據(jù)庫(kù)的體積。
基于日常使用場(chǎng)景的分析,大部分老數(shù)據(jù)讀取的頻率很低, 「 所以應(yīng)該提高最近一段時(shí)間的讀寫(xiě)效率 」 。對(duì)于這種情況,我們采取了 「 以時(shí)間和空間動(dòng)態(tài)劃分?jǐn)?shù)據(jù)庫(kù) 」 的方案。初始默認(rèn)值是每個(gè)數(shù)據(jù)庫(kù)存放半年的消息,超過(guò)時(shí)間之后新建一個(gè)數(shù)據(jù)庫(kù)存放。對(duì)于大部分使用場(chǎng)景,我們只需要讀寫(xiě)最新的數(shù)據(jù)庫(kù)就可以滿足需求,如果需要瀏覽更早的消息,可以再打開(kāi)之前的數(shù)據(jù)庫(kù)進(jìn)行讀取。
除了時(shí)間維度,我們還考慮了空間維度的劃分。如果半年內(nèi)消息普通消息規(guī)模超過(guò)閾值,也會(huì)新建一個(gè)數(shù)據(jù)庫(kù)進(jìn)行存儲(chǔ),讓每個(gè)數(shù)據(jù)庫(kù)大小和數(shù)據(jù)規(guī)模不至于太大,能提升最近一段時(shí)間消息的讀寫(xiě)效率。
2. 建立消息索引
對(duì)于最廣泛的使用場(chǎng)景,查看每一個(gè)聊天的消息,這種場(chǎng)景需要對(duì)每一個(gè)聊天會(huì)話建立一個(gè)索引。
這里的索引方案我們參考了安卓端: 「 即將每一個(gè)聊天轉(zhuǎn)換成一個(gè)數(shù)值型的ID,從而減少每條索引的長(zhǎng)度,提高索引的讀寫(xiě)效率 」 。除此之外,我們還對(duì)一些經(jīng)常訪問(wèn)的內(nèi)容,單獨(dú)提取成為一個(gè)字段,并且增加索引。比如消息的子類(lèi)型,這個(gè)在老數(shù)據(jù)庫(kù)中是一個(gè)序列化字段,沒(méi)有索引;但這個(gè)字段經(jīng)常需要用到,所以單獨(dú)提出成為一列,并且加上索引,為消息按類(lèi)型查找提供方便。
3. 消息體積優(yōu)化
消息總是會(huì)越來(lái)越多的,如何能夠不影響讀寫(xiě)效率的同時(shí),減少 & 壓縮體積,是我們的優(yōu)化方向。
從上面的數(shù)據(jù)看,部分消息體積較大,已經(jīng)超過(guò)了數(shù)據(jù)庫(kù)每頁(yè)的大小(Page Size)。數(shù)據(jù)庫(kù)是按頁(yè)存儲(chǔ)數(shù)據(jù)的,Page Size是數(shù)據(jù)庫(kù)一頁(yè)能夠容納的數(shù)據(jù)。如果一條數(shù)據(jù),一個(gè)頁(yè)放不下,就需要用到溢出頁(yè),把多出來(lái)放不下的數(shù)據(jù)放到溢出頁(yè)中,溢出頁(yè)可以有多個(gè)。這時(shí)候,如果讀取這條數(shù)據(jù),就需要把溢出頁(yè)也全部讀出來(lái),會(huì)增加IO的消耗。 「 如果壓縮數(shù)據(jù),能夠把消息體壓縮到一個(gè)頁(yè)能放得下,減少溢出頁(yè)的使用,是可以增加IO性能的 」 。
數(shù)據(jù)庫(kù)溢出頁(yè)結(jié)構(gòu)(來(lái)源:《The Definitive Guide to SQLite》)
但是壓縮需要占用CPU資源,這里選擇一種能夠平衡性能和壓縮率的算法是關(guān)鍵。
經(jīng)過(guò)對(duì)比壓縮算法的Benchmark,并且對(duì)消息體壓縮性進(jìn)行實(shí)測(cè), 「 最終選擇了一個(gè)高性能壓縮算法:lz4 」 。
經(jīng)過(guò)對(duì)測(cè)試帳號(hào)的數(shù)據(jù)分析,不同類(lèi)型的消息體大小差異較大,一般來(lái)說(shuō),文本消息的長(zhǎng)度不會(huì)特別大,但是網(wǎng)頁(yè)卡片類(lèi)型的消息,體積會(huì)較大。由于不同的消息長(zhǎng)度,獲得的壓縮率不一樣,太短的文本長(zhǎng)度,壓縮起來(lái)并沒(méi)有意義,所以經(jīng)過(guò)消息體長(zhǎng)度,壓縮率,壓縮性能的分析,最終確定對(duì)網(wǎng)頁(yè)卡片等進(jìn)行壓縮,在較低性能消耗的前提下, 「 綜合壓縮率可達(dá)到40%,減少了IO次數(shù) 」 。
4. 提高健壯性
如果數(shù)據(jù)庫(kù)文件由于外部原因發(fā)生損壞,則會(huì)對(duì)體驗(yàn)造成較大影響。降低損壞率和減少損壞帶來(lái)的數(shù)據(jù)損失,也是我們改進(jìn)的方向。
按照時(shí)間維度劃分?jǐn)?shù)據(jù)庫(kù)之后, 「 相當(dāng)于把消息按時(shí)間分散存儲(chǔ) 」 ,最新的數(shù)據(jù)庫(kù)負(fù)責(zé)讀寫(xiě)最近的消息,其余的數(shù)據(jù)庫(kù)只需要根據(jù)需求支持瀏覽查看消息。對(duì)于老數(shù)據(jù)庫(kù)而言,可以做到按需加載,從而減少了對(duì)數(shù)據(jù)庫(kù)的讀寫(xiě),也減少了這些數(shù)據(jù)庫(kù)損壞的幾率。一旦有數(shù)據(jù)庫(kù)出現(xiàn)損壞,即使無(wú)法恢復(fù),也不會(huì)所有消息全部丟失,只會(huì)丟失該數(shù)據(jù)庫(kù)對(duì)應(yīng)時(shí)間段的消息,這也可以減少部分?jǐn)?shù)據(jù)庫(kù)損壞帶來(lái)的損失。
在早期使用的單數(shù)據(jù)庫(kù)架構(gòu)中,由于數(shù)據(jù)會(huì)越攢越多,數(shù)據(jù)庫(kù)體積會(huì)持續(xù)變大,很難去做備份。分庫(kù)之后,每個(gè)數(shù)據(jù)庫(kù)體積變小,因而數(shù)據(jù)庫(kù)備份變得更為可行。因?yàn)樽钚碌臄?shù)據(jù)庫(kù)存在頻繁的消息讀寫(xiě),發(fā)生損壞的概率遠(yuǎn)高于老數(shù)據(jù)庫(kù),所以這里對(duì)最新的一個(gè)數(shù)據(jù)庫(kù)做定期的備份。 「 默認(rèn)配置下,我們每間隔一段時(shí)間會(huì)對(duì)最新的數(shù)據(jù)庫(kù)進(jìn)行一次備份,該備份是最新的一個(gè)數(shù)據(jù)庫(kù)的完整拷貝 」 。若最新的數(shù)據(jù)庫(kù)在讀寫(xiě)時(shí)發(fā)生損壞,會(huì)先嘗試從備份數(shù)據(jù)恢復(fù)。若恢復(fù)成功,則最多丟失從備份到恢復(fù)這段時(shí)間的數(shù)據(jù),進(jìn)一步降低損壞造成的損失。
優(yōu)化對(duì)比
經(jīng)過(guò)對(duì)比,對(duì)于一個(gè)在測(cè)試帳號(hào)中原始的消息數(shù)據(jù)庫(kù), 「 壓縮后大小可以減少接近一半,同時(shí)溢出頁(yè)數(shù)和需要使用溢出頁(yè)的記錄數(shù)減少也超過(guò)一半 」 。
對(duì)于讀寫(xiě)性能,對(duì)比壓縮前,壓縮后的讀取和解壓縮性能比之前 「 有接近10%的提升 」 。
后續(xù)我們微信客戶端團(tuán)隊(duì)將繼續(xù)研究數(shù)據(jù)庫(kù)修復(fù)相關(guān)的實(shí)踐,持續(xù)關(guān)注數(shù)據(jù)庫(kù)相關(guān)的性能數(shù)據(jù),提升可靠性,打造更好的用戶體驗(yàn)!
聲明:本站所有文章資源內(nèi)容,如無(wú)特殊說(shuō)明或標(biāo)注,均為采集網(wǎng)絡(luò)資源。如若本站內(nèi)容侵犯了原著者的合法權(quán)益,可聯(lián)系本站刪除。
