一、摘要
微信在立項(xiàng)之初,就已確立了利用數(shù)據(jù)版本號(hào)實(shí)現(xiàn)終端與后臺(tái)的數(shù)據(jù)增量同步機(jī)制,確保發(fā)消息時(shí)消息可靠送達(dá)對(duì)方手機(jī),避免了大量潛在的家庭糾紛。時(shí)至今日,微信已經(jīng)走過(guò)第五個(gè)年頭,這套同步機(jī)制仍然在消息收發(fā)、朋友圈通知、好友數(shù)據(jù)更新等需要數(shù)據(jù)同步的地方發(fā)揮著核心的作用。而在這同步機(jī)制的背后,需要一個(gè)高可用、高可靠的序列號(hào)生成器來(lái)產(chǎn)生同步數(shù)據(jù)用的版本號(hào)。這個(gè)序列號(hào)生成器我們稱(chēng)之為seqsvr,目前已經(jīng)發(fā)展為一個(gè)每天萬(wàn)億級(jí)調(diào)用的重量級(jí)系統(tǒng),其中每次申請(qǐng)序列號(hào)平時(shí)調(diào)用耗時(shí)1ms,99.9%的調(diào)用耗時(shí)小于3ms,服務(wù)部署于數(shù)百臺(tái)4核CPU服務(wù)器上。本文會(huì)重點(diǎn)介紹seqsvr的架構(gòu)核心思想,以及seqsvr隨著業(yè)務(wù)量快速上漲所做的架構(gòu)演變。
二、背景
微信服務(wù)器端為每一份需要與客戶(hù)端同步的數(shù)據(jù)(例如消息)都會(huì)賦予一個(gè)唯一的、遞增的序列號(hào)(后文稱(chēng)為sequence),作為這份數(shù)據(jù)的版本號(hào)。在客戶(hù)端與服務(wù)器端同步的時(shí)候,客戶(hù)端會(huì)帶上已經(jīng)同步下去數(shù)據(jù)的最大版本號(hào),后臺(tái)會(huì)根據(jù)客戶(hù)端最大版本號(hào)與服務(wù)器端的最大版本號(hào),計(jì)算出需要同步的增量數(shù)據(jù),返回給客戶(hù)端。這樣不僅保證了客戶(hù)端與服務(wù)器端的數(shù)據(jù)同步的可靠性,同時(shí)也大幅減少了同步時(shí)的冗余數(shù)據(jù)。
這里不用樂(lè)觀(guān)鎖機(jī)制來(lái)生成版本號(hào),而是使用了一個(gè)獨(dú)立的seqsvr來(lái)處理序列號(hào)操作,一方面因?yàn)闃I(yè)務(wù)有大量的sequence查詢(xún)需求——查詢(xún)已經(jīng)分配出去的最后一個(gè)sequence,而基于seqsvr的查詢(xún)操作可以做到非常輕量級(jí),避免對(duì)存儲(chǔ)層的大量IO查詢(xún)操作;另一方面微信用戶(hù)的不同種類(lèi)的數(shù)據(jù)存在不同的Key-Value系統(tǒng)中,使用統(tǒng)一的序列號(hào)有助于避免重復(fù)開(kāi)發(fā),同時(shí)業(yè)務(wù)邏輯可以很方便地判斷一個(gè)用戶(hù)的各類(lèi)數(shù)據(jù)是否有更新。
從seqsvr申請(qǐng)的、用作數(shù)據(jù)版本號(hào)的sequence,具有兩種基本的性質(zhì):
遞增的64位整型變量
每個(gè)用戶(hù)都有自己獨(dú)立的64位sequence空間
舉個(gè)例子,小明當(dāng)前申請(qǐng)的sequence為100,那么他下一次申請(qǐng)的sequence,可能為101,也可能是110,總之一定大于之前申請(qǐng)的100。而小紅呢,她的sequence與小明的sequence是獨(dú)立開(kāi)的,假如她當(dāng)前申請(qǐng)到的sequence為50,然后期間不管小明申請(qǐng)多少次sequence怎么折騰,都不會(huì)影響到她下一次申請(qǐng)到的值(很可能是51)。
這里用了每個(gè)用戶(hù)獨(dú)立的64位sequence的體系,而不是用一個(gè)全局的64位(或更高位)sequence,很大原因是全局唯一的sequence會(huì)有非常嚴(yán)重的申請(qǐng)互斥問(wèn)題,不容易去實(shí)現(xiàn)一個(gè)高性能高可靠的架構(gòu)。對(duì)微信業(yè)務(wù)來(lái)說(shuō),每個(gè)用戶(hù)獨(dú)立的64位sequence空間已經(jīng)滿(mǎn)足業(yè)務(wù)要求。
目前sequence用在終端與后臺(tái)的數(shù)據(jù)同步外,同時(shí)也廣泛用于微信后臺(tái)邏輯層的基礎(chǔ)數(shù)據(jù)一致性cache中,大幅減少邏輯層對(duì)存儲(chǔ)層的訪(fǎng)問(wèn)。雖然一個(gè)用于終端——后臺(tái)數(shù)據(jù)同步,一個(gè)用于后臺(tái)cache的一致性保證,場(chǎng)景大不相同。但我們仔細(xì)分析就會(huì)發(fā)現(xiàn),兩個(gè)場(chǎng)景都是利用sequence可靠遞增的性質(zhì)來(lái)實(shí)現(xiàn)數(shù)據(jù)的一致性保證,這就要求我們的seqsvr保證分配出去的sequence是穩(wěn)定遞增的,一旦出現(xiàn)回退必然導(dǎo)致各種數(shù)據(jù)錯(cuò)亂、消息消失;另外,這兩個(gè)場(chǎng)景都非常普遍,我們?cè)谑褂梦⑿诺臅r(shí)候會(huì)不知不覺(jué)地對(duì)應(yīng)到這兩個(gè)場(chǎng)景:小明給小紅發(fā)消息、小紅拉黑小明、小明發(fā)一條失戀狀態(tài)的朋友圈,一次簡(jiǎn)單的分手背后可能申請(qǐng)了無(wú)數(shù)次sequence。微信目前擁有數(shù)億的活躍用戶(hù),每時(shí)每刻都會(huì)有海量sequence申請(qǐng),這對(duì)seqsvr的設(shè)計(jì)也是個(gè)極大的挑戰(zhàn)。那么,既要sequence可靠遞增,又要能頂住海量的訪(fǎng)問(wèn),要如何設(shè)計(jì)seqsvr的架構(gòu)?我們先從seqsvr的架構(gòu)原型說(shuō)起。
三、架構(gòu)原型
不考慮seqsvr的具體架構(gòu)的話(huà),它應(yīng)該是一個(gè)巨大的64位數(shù)組,而我們每一個(gè)微信用戶(hù),都在這個(gè)大數(shù)組里獨(dú)占一格8bytes的空間,這個(gè)格子就放著用戶(hù)已經(jīng)分配出去的最后一個(gè)sequence:cur_seq。每個(gè)用戶(hù)來(lái)申請(qǐng)sequence的時(shí)候,只需要將用戶(hù)的cur_seq+=1,保存回?cái)?shù)組,并返回給用戶(hù)。

圖1. 小明申請(qǐng)了一個(gè)sequence,返回101
預(yù)分配中間層
任何一件看起來(lái)很簡(jiǎn)單的事,在海量的訪(fǎng)問(wèn)量下都會(huì)變得不簡(jiǎn)單。前文提到,seqsvr需要保證分配出去的sequence遞增(數(shù)據(jù)可靠),還需要滿(mǎn)足海量的訪(fǎng)問(wèn)量(每天接近萬(wàn)億級(jí)別的訪(fǎng)問(wèn))。滿(mǎn)足數(shù)據(jù)可靠的話(huà),我們很容易想到把數(shù)據(jù)持久化到硬盤(pán),但是按照目前每秒千萬(wàn)級(jí)的訪(fǎng)問(wèn)量(~10^7 QPS),基本沒(méi)有任何硬盤(pán)系統(tǒng)能扛住。