成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

計數系統架構實踐一次搞定

開發 開發工具
很多業務都有“計數”需求,今天我們就以微博為例,講述計數系統架構實踐的過程。

一、需求緣起

很多業務都有“計數”需求,以微博為例:

微博首頁的個人中心部分

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

  • 關注了多少人的計數
  • 粉絲的計數
  • 發布博文的計數

微博首頁的博文消息主體部分

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

  • 轉發計數
  • 評論計數
  • 點贊計數
  • 甚至是瀏覽計數

在業務復雜,計數擴展頻繁,數據量大,并發量大的情況下,計數系統的架構演進與實踐,是本文將要討論的問題。

二、業務分析與計數初步實現

典型的互聯網架構

典型的互聯網架構,常常分為這么幾層:

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

針對“緣起”里微博計數的例子,主要涉及“關注”業務,“粉絲”業務,“微博消息”業務,一般來說,會有相應的db存儲相關數據,相應的service提供相關業務的RPC接口:

“緣起”里微博計數的例子

  • 關注服務:提供關注數據的增刪查改RPC接口
  • 粉絲服務:提供粉絲數據的增刪查改RPC接口
  • 消息服務:提供微博消息數據的增刪查改RPC接口,消息業務相對比較復雜,涉及微博消息、轉發、評論、點贊等數據的存儲

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

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

計數數據

例如,個人中心首頁,需要展現博文數量這個計數,web層訪問message-service的count接口,這個接口執行:

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

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

這個方案叫做“count”計數法,在數據量并發量不大的情況下,最容易想到且最經常使用的就是這種方法,但隨著數據量的上升,并發量的上升,這個方法的弊端將逐步展現。

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

整個拉取計數的偽代碼如下:

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

其中:

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

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

“count”計數法方案,可以總結為:

  • 多條消息多次查詢,for循環進行
  • 一條消息多次查詢,多個計數的查詢
  • 一次查詢一個count,每個計數都是一個count語句

那如何進行優化呢?

三、計數外置的架構設計

計數是一個通用的需求,有沒有可能,這個計數的需求實現在一個通用的系統里,而不是由關注服務、粉絲服務、微博服務來分別來提供相應的功能呢(否則擴展性極差)?

這樣需要實現一個通用的計數服務。

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

  • 用戶(uid)維度的計數:用戶的關注計數,粉絲計數,發布的微博計數
  • 微博消息(msg_id)維度的計數:消息轉發計數,評論計數,點贊計數

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

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

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

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

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

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

計數外置的架構設計

這樣,在查詢一條微博消息的若干個計數的時候,不用進行多次數據庫count操作,而會轉變為一條數據的多個屬性的查詢:

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

甚至,可以將微博首頁所有消息的計數,轉變為一條IN語句(不用多次查詢了)的批量查詢:

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

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

方案非常帥氣,接下來,問題轉化為:當有微博被轉發、評論、點贊的時候,計數服務如何同步的進行計數的變更呢?

如果讓業務服務來調用計數服務,勢必會導致業務系統與計數系統耦合。

之前的文章介紹過,對于不關心下游結果的業務,可以使用MQ來解耦(具體請查閱《到底什么時候該使用MQ?》),在業務發生變化的時候,向MQ發送一條異步消息,通知計數系統計數發生了變化即可:

向MQ發送一條異步消息

如上圖:

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

這個方案稱為“計數外置”,可以總結為:

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

計數外置,本質是數據的冗余,架構設計上,數據冗余必將引發數據的一致性問題,需要有機制來保證計數系統里的數據與業務系統里的數據一致,常見的方法有:

  • 對于一致性要求比較高的業務,要有定期check并fix的機制,例如關注計數,粉絲計數,微博消息計數等
  • 對于一致性要求比較低的業務,即使有數據不一致,業務可以接受,例如微博瀏覽數,微博轉發數等

四、計數外置緩存優化

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

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

但是,緩存是kv結構的,無法像數據庫一樣,設置成t_uid_count(uid, c1, c2, c3)這樣的schema,如何來對kv進行設計呢?

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

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

uid=123的用戶

此時對應的counting-service架構變為:

如此這般,多個uid的多個計數,又可能會變為多次緩存的訪問:

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

這個“計數外置緩存優化”方案,可以總結為:

  • 使用緩存來保存讀多寫少的計數(其實寫多讀少,一致性要求不高的計數,也可以先用緩存保存,然后定期刷到數據庫中,以降低數據庫的讀寫壓力)
  • 使用id:type的方式作為緩存的key,使用count來作為緩存的value
  • 多次讀取緩存來查詢多個uid的計數

五、緩存批量讀取優化

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

當當當當!

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

緩存kv結構的key是uid,value可以是多個計數同時存儲。

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

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

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

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

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

這個“計數外置緩存批量優化”方案,可以總結為:

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

六、計數擴展性優化

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

之前的數據庫結構是:

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

這種設計,通過列來進行計數的存儲,如果增加一個XX計數,數據庫的表結構要變更為:

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

在數據量很大的情況下,頻繁的變更數據庫schema的結構顯然是不可取的,有沒有擴展性更好的方式呢?

當當當當!

不要陷入思維定式,誰說只能通過擴展列來擴展屬性,通過擴展行來擴展屬性,在“架構師之路”的系列文章里也不是***次出現了(具體請查閱《啥,又要為表增加一列屬性?》《這才是真正的表擴展方案》《100億數據1萬屬性數據架構設計》),完全可以這樣設計表結構:

  1. t_user_count(uid, count_key, count_value) 

如果需要新增一個計數XX_count,只需要增加一行即可,而不需要變更表結構:

七、總結

小小的計數,在數據量大,并發量大的時候,其架構實踐思路為:

  • 計數外置:由“count計數法”升級為“計數外置法”
  • 讀多寫少,甚至寫多但一致性要求不高的計數,需要進行緩存優化,降低數據庫壓力
  • 緩存kv設計優化,可以由[key:type]->[count],優化為[key]->[c1:c2:c3]

即:

優化為:

數據庫擴展性優化,可以由列擴展優化為行擴展

即:

優化為:

 

計數系統架構先聊到這里,希望大家有收獲。

【本文為51CTO專欄作者“58沈劍”原創稿件,轉載請聯系原作者】

戳這里,看該作者更多好文

責任編輯:趙寧寧 來源: 51CTO專欄
相關推薦

2018-05-25 14:41:56

Serverless無服務器構造

2017-12-07 12:47:48

Serverless架構基因

2020-07-14 15:10:21

Redis架構代碼

2017-07-11 16:44:04

數據庫水平切分架構

2017-08-11 13:55:13

數據庫水平切分架構

2023-11-06 07:45:42

單據圖片處理

2010-04-01 22:16:21

2022-03-23 15:43:26

Android客戶端架構

2019-04-18 14:06:35

MySQL分庫分表數據庫

2019-01-21 11:17:13

CPU優化定位

2020-08-19 11:02:39

系統ssh登錄

2013-04-02 14:27:02

架構架構評審

2020-10-15 14:05:30

PostgreSQL升級開發

2024-09-26 10:41:31

2023-10-23 18:50:52

webservice

2011-06-28 10:41:50

DBA

2025-04-09 08:00:00

FastAPI統一響應全局異常處理

2024-12-17 14:52:46

2023-06-07 07:31:04

PC端app脫殼技巧

2019-09-12 09:40:34

秒殺系統高并發
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 91porn在线| 看片天堂| 国产一级视频 | 精品国产不卡一区二区三区 | 亚洲一区二区三区免费视频 | 国内精品成人 | 亚洲成a| 色婷婷av777 av免费网站在线 | 国产三区在线观看视频 | 日韩免费视频一区二区 | 免费观看黄色一级片 | 国产成人99久久亚洲综合精品 | 蜜臀av日日欢夜夜爽一区 | 精品欧美黑人一区二区三区 | 色偷偷人人澡人人爽人人模 | 欧美综合在线视频 | 久久99国产精一区二区三区 | 91福利网| 国产精品久久久久久久久图文区 | 国产不卡在线播放 | 欧美精品网 | 国产一区二区在线免费 | 亚洲午夜视频在线观看 | 91p在线观看 | 蜜月aⅴ免费一区二区三区 99re在线视频 | 欧美a∨| 色香蕉在线 | 欧美精品成人一区二区三区四区 | 日本在线综合 | 性欧美hd | 少妇精品久久久久久久久久 | 中文字幕日韩三级 | 亚洲欧美日韩在线 | 国产精品高清在线 | 成人一区精品 | 七七婷婷婷婷精品国产 | 久久精品成人 | 亚洲精品久久久久久久久久久久久 | 九色国产| 911精品国产 | 久久久999免费视频 999久久久久久久久6666 |