日韩无码专区无码一级三级片|91人人爱网站中日韩无码电影|厨房大战丰满熟妇|AV高清无码在线免费观看|另类AV日韩少妇熟女|中文日本大黄一级黄色片|色情在线视频免费|亚洲成人特黄a片|黄片wwwav色图欧美|欧亚乱色一区二区三区

RELATEED CONSULTING
相關咨詢
選擇下列產(chǎn)品馬上在線溝通
服務時間:8:30-17:00
你可能遇到了下面的問題
關閉右側(cè)工具欄

新聞中心

這里有您想知道的互聯(lián)網(wǎng)營銷解決方案
每秒100W次的計數(shù),架構(gòu)原來可以這樣設計!

今天和大家聊聊計數(shù)系統(tǒng)。

成都創(chuàng)新互聯(lián)長期為上1000+客戶提供的網(wǎng)站建設服務,團隊從業(yè)經(jīng)驗10年,關注不同地域、不同群體,并針對不同對象提供差異化的產(chǎn)品和服務;打造開放共贏平臺,與合作伙伴共同營造健康的互聯(lián)網(wǎng)生態(tài)環(huán)境。為柘城企業(yè)提供專業(yè)的成都網(wǎng)站建設、網(wǎng)站建設,柘城網(wǎng)站改版等技術服務。擁有十多年豐富建站經(jīng)驗和眾多成功案例,為您定制開發(fā)。

畫外音:文章較長,可提前收藏。

計數(shù)系統(tǒng)解決什么業(yè)務問題?

很多業(yè)務都有“計數(shù)”需求,以微博為例:

微博首頁的個人中心部分,有三個重要的計數(shù):

  • 關注了多少人的計數(shù);
  • 粉絲的計數(shù);
  • 發(fā)布博文的計數(shù);

微博首頁的博文消息主體部分,也有有很多計數(shù),分別是一條博文的:

  • 轉(zhuǎn)發(fā)計數(shù);
  • 評論計數(shù);
  • 點贊計數(shù);
  • 甚至是瀏覽計數(shù);

畫外音:瀏覽計數(shù)有點難,每秒100W次PV,就有100W次計數(shù)。

在業(yè)務復雜,計數(shù)擴展頻繁,數(shù)據(jù)量大,并發(fā)量大的情況下,計數(shù)系統(tǒng)的架構(gòu)演進與實踐,是本文將要分享的問題。

如何最快速的實現(xiàn)業(yè)務計數(shù)呢?

count計數(shù)法。

典型的互聯(lián)網(wǎng)架構(gòu),常常分為這么幾層:

  • 調(diào)用層:處于端上的browser或者APP;
  • 站點層:拼裝html或者json返回的web-server層;
  • 服務層:提供RPC調(diào)用接口的service層;
  • 數(shù)據(jù)層:提供固化數(shù)據(jù)存儲的db,以及加速存儲的cache;

針對上文微博計數(shù)的例子,主要涉及“關注”業(yè)務,“粉絲”業(yè)務,“微博消息”業(yè)務,一般來說,會有相應的db存儲相關數(shù)據(jù),相應的service提供相關業(yè)務的RPC接口:

  • 關注服務:提供關注數(shù)據(jù)的增刪查改RPC接口;
  • 粉絲服務:提供粉絲數(shù)據(jù)的增刪查改RPC接口;
  • 消息服務:提供微博消息數(shù)據(jù)的增刪查改RPC接口,消息業(yè)務相對比較復雜,涉及微博消息、轉(zhuǎn)發(fā)、評論、點贊等數(shù)據(jù)的存儲;

對關注、粉絲、微博業(yè)務進行了初步解析,那首頁的計數(shù)需求應該如何滿足呢?

很容易想到,關注服務+粉絲服務+消息服務均提供相應接口,就能拿到相關計數(shù)數(shù)據(jù)。

例如,個人中心首頁,需要展現(xiàn)博文數(shù)量這個計數(shù),web層訪問message-service的count接口,這個接口執(zhí)行:

 
 
 
 
  1. select count(*) from t_msg where uid = XXX

同理,也很容易拿到關注,粉絲的這些計數(shù)。

這個方案叫做“count”計數(shù)法,在數(shù)據(jù)量并發(fā)量不大的情況下,最容易想到且最經(jīng)常使用的就是這種方法。

count計數(shù)法存在什么問題呢?

隨著數(shù)據(jù)量的上升,并發(fā)量的上升,這個方法的弊端將逐步展現(xiàn)。

例如,微博首頁有很多條微博消息,每條消息有若干計數(shù),此時計數(shù)的拉取就成了一個龐大的工程:

整個拉取計數(shù)的偽代碼如下:

 
 
 
 
  1. list = getHomePageMsg(uid);// 獲取首頁所有消息
  2. for( msg_id in list){ // 對每一條消息
  3.          getReadCount(msg_id);  // 閱讀計數(shù)
  4.          getForwordCount(msg_id); // 轉(zhuǎn)發(fā)計數(shù)
  5.          getCommentCount(msg_id); // 評論計數(shù)
  6.          getPraiseCount(msg_id); // 贊計數(shù)
  7. }

其中:

  • 每一個微博消息的若干個計數(shù),都對應4個后端服務訪問;
  • 每一個訪問,對應一條count的數(shù)據(jù)庫訪問(count要了老命了);

其效率之低,資源消耗之大,處理時間之長,可想而知。

“count”計數(shù)法方案,可以總結(jié)為:

  • 多條消息多次查詢,for循環(huán)進行;
  • 一條消息多次查詢,多個計數(shù)的查詢;
  • 一次查詢一個count,每個計數(shù)都是一個count語句;

那如何進行優(yōu)化呢?

計數(shù)外置法。

計數(shù)是一個通用的需求,有沒有可能,這個計數(shù)的需求實現(xiàn)在一個通用的系統(tǒng)里,而不是由關注服務、粉絲服務、微博服務來分別來提供相應的功能呢?

畫外音:各個業(yè)務系統(tǒng)具備的通用痛點,應該下沉統(tǒng)一解決。

可以抽象一個通用的計數(shù)服務。

通過分析,上述微博的業(yè)務可以抽象成兩類:

  • 用戶(uid)維度的計數(shù):用戶的關注計數(shù),粉絲計數(shù),發(fā)布的微博計數(shù);
  • 微博消息(msg_id)維度的計數(shù):消息轉(zhuǎn)發(fā)計數(shù),評論計數(shù),點贊計數(shù);

于是可以抽象出兩個表,針對這兩個維度來進行計數(shù)的存儲:

 
 
 
 
  1. t_user_count (uid, gz_count, fs_count, wb_count);
  2. t_msg_count (msg_id, forword_count, comment_count, praise_count);

甚至可以更為抽象,一個表搞定所有計數(shù):

 
 
 
 
  1. t_count(id, type, c1, c2, c3, …)

通過type來判斷,id究竟是uid還是msg_id,但并不建議這么做。

存儲抽象完,再抽象出一個計數(shù)服務對這些數(shù)據(jù)進行管理,提供友善的RPC接口:

這樣,在查詢一條微博消息的若干個計數(shù)的時候,不用進行多次數(shù)據(jù)庫count操作,而會轉(zhuǎn)變?yōu)橐粭l數(shù)據(jù)的多個屬性的查詢:

 
 
 
 
  1. for(msg_id in list) {
  2. select forword_count, comment_count, praise_count 
  3.     from t_msg_count 
  4.     where msg_id=$msg_id;
  5. }

甚至,可以將微博首頁所有消息的計數(shù),轉(zhuǎn)變?yōu)橐粭lIN語句(不用多次查詢了)的批量查詢:

 
 
 
 
  1. select * from t_msg_count 
  2.     where msg_id IN
  3.     ($msg_id1, $msg_id2, $msg_id3, …);

IN查詢可以命中msg_id聚集索引,效率很高。

那么,當有微博被轉(zhuǎn)發(fā)、評論、點贊的時候,計數(shù)服務如何同步的進行計數(shù)的變更呢?

如果讓業(yè)務服務來調(diào)用計數(shù)服務,勢必會導致業(yè)務系統(tǒng)與計數(shù)系統(tǒng)耦合??梢酝ㄟ^MQ來解耦,在業(yè)務發(fā)生變化的時候,向MQ發(fā)送一條異步消息,通知計數(shù)系統(tǒng)計數(shù)發(fā)生了變化即可:

如上圖:

  • 用戶新發(fā)布了一條微博;
  • msg-service向MQ發(fā)送一條消息;
  • counting-service從MQ接收消息;
  • counting-service變更這個uid發(fā)布微博消息計數(shù);

畫外音:其實發(fā)送一條微博本來就會發(fā)MQ消息,計數(shù)系統(tǒng)只是新增一個訂閱方。

這個方案稱為“計數(shù)外置”,可以總結(jié)為:

  • 通過counting-service單獨保存計數(shù);
  • MQ同步計數(shù)的變更;
  • 多條消息的多個計數(shù),一個批量IN查詢完成;

計數(shù)外置可能存在什么新的問題呢?

計數(shù)外置,本質(zhì)是數(shù)據(jù)的冗余,架構(gòu)設計上,數(shù)據(jù)冗余必將引發(fā)數(shù)據(jù)的一致性問題,需要有機制來保證計數(shù)系統(tǒng)里的數(shù)據(jù)與業(yè)務系統(tǒng)里的數(shù)據(jù)一致,常見的方法有:

  • 對于一致性要求比較高的業(yè)務,要有定期check并fix的機制,例如關注計數(shù),粉絲計數(shù),微博消息計數(shù)等;
  • 對于一致性要求比較低的業(yè)務,即使有數(shù)據(jù)不一致,業(yè)務可以接受,例如微博瀏覽數(shù),微博轉(zhuǎn)發(fā)數(shù)等;

計數(shù)外置很大程度上解決了計數(shù)存取的性能問題,但是否還有優(yōu)化空間呢?

像關注計數(shù),粉絲計數(shù),微博消息計數(shù),變化的頻率很低,查詢的頻率很高,這類讀多些少的業(yè)務場景,非常適合使用緩存來進行查詢優(yōu)化,減少數(shù)據(jù)庫的查詢次數(shù),降低數(shù)據(jù)庫的壓力。

但是,緩存是kv結(jié)構(gòu)的,無法像數(shù)據(jù)庫一樣,設置成

 
 
 
 
  1. t_uid_count(uid, c1, c2, c3)

這樣的schema,如何來對kv進行設計呢?

緩存kv結(jié)構(gòu)的value是計數(shù),看來只能在key上做設計,很容易想到,可以使用uid:type來做key,存儲對應type的計數(shù)。

對于uid=123的用戶,其關注計數(shù),粉絲計數(shù),微博消息計數(shù)的緩存就可以設計為:

此時對應的counting-service架構(gòu)變?yōu)椋?/p>

如此這般,多個uid的多個計數(shù),又可能會變?yōu)槎啻尉彺娴脑L問:

 
 
 
 
  1. for(uid in list) {
  2.  memcache::get($uid:c1, $uid:c2, $uid:c3);
  3. }

這個“計數(shù)外置緩存優(yōu)化”方案,可以總結(jié)為:

  • 使用緩存來保存讀多寫少的計數(shù);

畫外音:其實寫多讀少,一致性要求不高的計數(shù),也可以先用緩存保存,然后定期刷到數(shù)據(jù)庫中,以降低數(shù)據(jù)庫的讀寫壓力。

  • 使用id:type的方式作為緩存的key,使用count來作為緩存的value;
  • 多次讀取緩存來查詢多個uid的計數(shù);

緩存的使用能夠極大降低數(shù)據(jù)庫的壓力,但多次緩存交互依舊存在優(yōu)化空間,有沒有辦法進一步優(yōu)化呢?

不要陷入思維定式,誰說value一定只能是一個計數(shù),難道不能多個計數(shù)存儲在一個value中么?

緩存kv結(jié)構(gòu)的key是uid,value可以是多個計數(shù)同時存儲。

對于uid=123的用戶,其關注計數(shù),粉絲計數(shù),微博消息計數(shù)的緩存就可以設計為:

這樣多個用戶,多個計數(shù)的查詢就可以一次搞定:

 
 
 
 
  1. memcache::get($uid1, $uid2, $uid3, …);

然后對獲取的value進行分析,得到關注計數(shù),粉絲計數(shù),微博計數(shù)。

如果計數(shù)value能夠事先預估一個范圍,甚至可以用一個整數(shù)的不同bit來存儲多個計數(shù),用整數(shù)的與或非計算提高效率。

這個“計數(shù)外置緩存批量優(yōu)化”方案,可以總結(jié)為:

  • 使用id作為key,使用同一個id的多個計數(shù)的拼接作為value;
  • 多個id的多個計數(shù)查詢,一次搞定;

考慮完效率,架構(gòu)設計上還需要考慮擴展性,如果uid除了關注計數(shù),粉絲計數(shù),微博計數(shù),還要增加一個計數(shù),這時系統(tǒng)需要做什么變更呢?

之前的數(shù)據(jù)庫結(jié)構(gòu)是:

 
 
 
 
  1. t_user_count(uid, gz_count, fs_count, wb_count)

這種設計,通過列來進行計數(shù)的存儲,如果增加一個XX計數(shù),數(shù)據(jù)庫的表結(jié)構(gòu)要變更為:

 
 
 
 
  1. t_user_count(uid, gz_count, fs_count, wb_count, XX_count)

在數(shù)據(jù)量很大的情況下,頻繁的變更數(shù)據(jù)庫schema的結(jié)構(gòu)顯然是不可取的,有沒有擴展性更好的方式呢?

不要陷入思維定式,誰說只能通過擴展列來擴展屬性,能不能通過擴展行來擴展屬性呢?

答案是肯定的,完全可以這樣設計表結(jié)構(gòu):

 
 
 
 
  1. t_user_count(uid, count_key, count_value)

如果需要新增一個計數(shù)XX_count,只需要增加一行即可,而不需要變更表結(jié)構(gòu):

總結(jié)

小小的計數(shù),在數(shù)據(jù)量大,并發(fā)量大的時候,其架構(gòu)實踐思路為:

(1)計數(shù)外置:由“count計數(shù)法”升級為“計數(shù)外置法”;

(2)讀多寫少,甚至寫多但一致性要求不高的計數(shù),需要進行緩存優(yōu)化,降低數(shù)據(jù)庫壓力;

(3)緩存kv設計優(yōu)化,可以由[key:type]->[count],優(yōu)化為[key]->[c1:c2:c3]即:

優(yōu)化為:

(4)數(shù)據(jù)庫擴展性優(yōu)化,可以由列擴展優(yōu)化為行擴展即:

優(yōu)化為:

【本文為專欄作者“58沈劍”原創(chuàng)稿件,轉(zhuǎn)載請聯(lián)系原作者】


分享文章:每秒100W次的計數(shù),架構(gòu)原來可以這樣設計!
標題路徑:http://m.5511xx.com/article/cdjpopj.html