實(shí)驗(yàn)結(jié)果圖中的毫秒時(shí)間為單條記錄的壓縮或解壓縮時(shí)間。壓縮比的計(jì)算方式為壓縮前字節(jié)碼長(zhǎng)度/壓縮后字節(jié)碼長(zhǎng)度??梢钥闯?,所有壓縮算法的壓縮/解壓時(shí)間都會(huì)隨著壓縮比的上升而整體呈上升趨勢(shì)。其中LZ4的Java Unsafe、Java Safe版由于考慮平臺(tái)兼容性問(wèn)題,出現(xiàn)了明顯的速度異常。
從使用場(chǎng)景(一次全量寫(xiě)入,多次逐條讀?。┏霭l(fā),特征系統(tǒng)主要的服務(wù)指標(biāo)是特征高并發(fā)下的響應(yīng)時(shí)間與特征數(shù)據(jù)存儲(chǔ)效率。因此特征壓縮關(guān)注的指標(biāo)其實(shí)是:快速的解壓速度與較高的壓縮比,而對(duì)壓縮速度其實(shí)要求不高。因此綜合上述實(shí)驗(yàn)中各個(gè)算法的表現(xiàn),Snappy是較為合適我們的需求。
2.2.3 字典壓縮
壓縮的本質(zhì)是利用共性,在不影響信息量的情況下進(jìn)行重新編碼,以縮減空間占用。上節(jié)中的字節(jié)壓縮是單行壓縮,因此只能運(yùn)用到同一條記錄中的共性,而無(wú)法顧及全局共性。舉個(gè)例子:假設(shè)某個(gè)用戶(hù)維度特征所有用戶(hù)的特征值是完全一樣的,字節(jié)壓縮逐條壓縮不能節(jié)省任何的存儲(chǔ)空間,而我們卻知道實(shí)際上只有一個(gè)重復(fù)的值在反復(fù)出現(xiàn)。即便是單條記錄內(nèi)部,由于壓縮算法窗口大小的限制,長(zhǎng)Pattern也很難被顧及到。因此,對(duì)全局的特征值做一次字典統(tǒng)計(jì),自動(dòng)或人工的將頻繁P(pán)attern加入到字典并重新編碼,能夠解決短文本字節(jié)壓縮的局限性。
2.3 數(shù)據(jù)同步
當(dāng)每次請(qǐng)求,策略計(jì)算需要大量的特征數(shù)據(jù)時(shí)(比如一次請(qǐng)求上千條的廣告商特征),我們需要非常強(qiáng)悍的在線數(shù)據(jù)獲取能力。而在存儲(chǔ)特征的不同方法中,訪問(wèn)本地內(nèi)存毫無(wú)疑問(wèn)是性能最佳的解決方式。想要在本地內(nèi)存中訪問(wèn)到特征數(shù)據(jù),通常我們有兩種有效手段:內(nèi)存副本和客戶(hù)端緩存。
2.3.1 內(nèi)存副本技術(shù)
當(dāng)數(shù)據(jù)總量不大時(shí),策略使用方可以在本地完全鏡像一份特征數(shù)據(jù),這份鏡像叫內(nèi)存副本。使用內(nèi)存副本和使用本地的數(shù)據(jù)完全一致,使用者無(wú)需關(guān)心遠(yuǎn)端數(shù)據(jù)源的存在。內(nèi)存副本需要和數(shù)據(jù)源通過(guò)某些協(xié)議進(jìn)行同步更新,這類(lèi)同步技術(shù)稱(chēng)為內(nèi)存副本技術(shù)。在線特征系統(tǒng)的場(chǎng)景中,數(shù)據(jù)源可以抽象為一個(gè)KV類(lèi)型的數(shù)據(jù)集,內(nèi)存副本技術(shù)需要把這樣一個(gè)數(shù)據(jù)集完整的同步到內(nèi)存副本中。
推拉結(jié)合——時(shí)效性和一致性
一般來(lái)說(shuō),數(shù)據(jù)同步為兩種類(lèi)型:推(Push)和拉(Pull)。Push的技術(shù)比較簡(jiǎn)單,依賴(lài)目前常見(jiàn)的消息隊(duì)列中間件,可以根據(jù)需求做到將一個(gè)數(shù)據(jù)變化傳送到一個(gè)內(nèi)存副本中。但是,即使實(shí)現(xiàn)了不重不漏的高可靠性消息隊(duì)列通知(通常代價(jià)很大),也還面臨著初始化啟動(dòng)時(shí)批量數(shù)據(jù)同步的問(wèn)題——所以,Push只能作為一種提高內(nèi)存副本時(shí)效性的手段,本質(zhì)上內(nèi)存副本同步還得依賴(lài)Pull協(xié)議。Pull類(lèi)的同步協(xié)議有一個(gè)非常好的特性就是冪等,一次失敗或成功的同步不會(huì)影響下一次進(jìn)行新的同步。
Pull協(xié)議有非常多的選擇,最簡(jiǎn)單的每次將所有數(shù)據(jù)全量拉走就是一種基礎(chǔ)協(xié)議。但是在業(yè)務(wù)需求中需要追求數(shù)據(jù)同步效率,所以用一些比較高效的Pull協(xié)議就很重要。為了縮減拉取數(shù)據(jù)量,這些協(xié)議本質(zhì)上來(lái)說(shuō)都是希望高效的計(jì)算出盡量精確的數(shù)據(jù)差異(Diff),然后同步這些必要的數(shù)據(jù)變動(dòng)。這里介紹兩種我們?cè)?jīng)在工程實(shí)踐中應(yīng)用過(guò)的Pull型數(shù)據(jù)同步協(xié)議。
基于版本號(hào)同步——回放日志(RedoLog)和退化算法
在數(shù)據(jù)源更新時(shí),對(duì)于每一次數(shù)據(jù)變化,基于版本號(hào)的同步算法會(huì)為這次變化分配一個(gè)唯一的遞增版本號(hào),并使用一個(gè)更新隊(duì)列記錄所有版本號(hào)對(duì)應(yīng)的數(shù)據(jù)變化。
內(nèi)存副本發(fā)起同步請(qǐng)求時(shí),會(huì)攜帶該副本上一次完成同步時(shí)的最大版本號(hào),這意味著所有該版本號(hào)之后的數(shù)據(jù)變化都需要被拉取過(guò)來(lái)。數(shù)據(jù)源方收到請(qǐng)求后,從更新隊(duì)列中找到大于該版本號(hào)的所有數(shù)據(jù)變化,并將數(shù)據(jù)變化匯總,得到最終需要更新的Diff,返回給發(fā)起方。此時(shí)內(nèi)存副本只需要更新這些Diff數(shù)據(jù)即可。
對(duì)于大多數(shù)的業(yè)務(wù)場(chǎng)景,特征數(shù)據(jù)的生成會(huì)收口到一個(gè)統(tǒng)一的更新服務(wù)中,所以遞增版本號(hào)可以串行的生成。如果在分布式的數(shù)據(jù)更新環(huán)境中,則需要利用分布式id生成器來(lái)獲取遞增版本號(hào)。