繼續答星球水友提問, 30WQPS的點贊計數業務,如何設計?
可以看到,這個 業務的特點 是:
(1) 吞吐量超高 ;
(2) 能夠接受一定數據不一致 ;
畫外音: 計數有微小不準確,不是大問題。
先用最樸素的思想, 只考慮點贊計數,可以怎麼做?
有幾點是最容易想到的:
(1)肯定 不能 用 數據庫抗 實時讀寫流量;
(2)redis天然支持固化, 可以用高可用redis集群來做固化存儲 ;
(3) 也可以用MySQL來做固化存儲 , redis做緩存 ,讀寫操作都落緩存, 異步線程定期刷DB ;
(4)架一層 計數服務 ,將計數與業務邏輯解耦;
此時 MySQL核心數據結構 是:
t_count(msg_id, praise_count)
此時 redis的KV設計 也不難:
key: msg_id
value: praise_count
繼續答星球水友提問, 30WQPS的點贊計數業務,如何設計?
可以看到,這個 業務的特點 是:
(1) 吞吐量超高 ;
(2) 能夠接受一定數據不一致 ;
畫外音: 計數有微小不準確,不是大問題。
先用最樸素的思想, 只考慮點贊計數,可以怎麼做?
有幾點是最容易想到的:
(1)肯定 不能 用 數據庫抗 實時讀寫流量;
(2)redis天然支持固化, 可以用高可用redis集群來做固化存儲 ;
(3) 也可以用MySQL來做固化存儲 , redis做緩存 ,讀寫操作都落緩存, 異步線程定期刷DB ;
(4)架一層 計數服務 ,將計數與業務邏輯解耦;
此時 MySQL核心數據結構 是:
t_count(msg_id, praise_count)
此時 redis的KV設計 也不難:
key: msg_id
value: praise_count
似乎很容易就搞定了:
(1)服務可以水平擴展;
(2)數據量增加時,數據庫可以水平擴展;
(3)讀寫量增加時,緩存也可以水平擴展;
計數系統的 難點 ,還在於 業務擴展性 問題,以及 效率 問題。
以微博為例:
繼續答星球水友提問, 30WQPS的點贊計數業務,如何設計?
可以看到,這個 業務的特點 是:
(1) 吞吐量超高 ;
(2) 能夠接受一定數據不一致 ;
畫外音: 計數有微小不準確,不是大問題。
先用最樸素的思想, 只考慮點贊計數,可以怎麼做?
有幾點是最容易想到的:
(1)肯定 不能 用 數據庫抗 實時讀寫流量;
(2)redis天然支持固化, 可以用高可用redis集群來做固化存儲 ;
(3) 也可以用MySQL來做固化存儲 , redis做緩存 ,讀寫操作都落緩存, 異步線程定期刷DB ;
(4)架一層 計數服務 ,將計數與業務邏輯解耦;
此時 MySQL核心數據結構 是:
t_count(msg_id, praise_count)
此時 redis的KV設計 也不難:
key: msg_id
value: praise_count
似乎很容易就搞定了:
(1)服務可以水平擴展;
(2)數據量增加時,數據庫可以水平擴展;
(3)讀寫量增加時,緩存也可以水平擴展;
計數系統的 難點 ,還在於 業務擴展性 問題,以及 效率 問題。
以微博為例:
(1)用戶微博首頁, 有多條消息 list<msg_id> ,這是一種 擴展 ;
(2)同一條消息msg_id, 不止有點贊計數,還有閱讀計數,轉發計數,評論計數 ,這也是一種 擴展 ;
假如用最樸素的方式實現,多條消息多個計數的獲取偽代碼如下:
// (1) 獲取首頁所有消息 msg_id
list<msg_id> = getHomePageMsg(uid);
// (2)對於首頁的所有消息要 拉取多個計數
for( msg_id in list<msg_id>){
//(3.1)獲取 閱讀計數
getReadCount(msg_id);
//(3.2)獲取 轉發計數
getForwordCount(msg_id);
//(3.3)獲取 評論計數
getCommentCount(msg_id);
//(3.4)獲取 贊計數
getPraiseCount(msg_id);
}
由於同一個msg_id多了幾種業務計數, redis的key需要帶上業務flag ,升級為:
msg_id:read
msg_id:forword
msg_id:comment
msg_id:praise
用來區分共一個msg_id的四種不同業務計數,redis不能支持key的模糊操作,必須訪問四次reids。
假設首頁有100條消息, 這個方案 總結為:
(1)for循環每一條消息,100條消息100次;
(2)每條消息4次RPC獲取計數接口調用;
(3)每次調用服務要訪問reids,拼裝key獲取count;
畫外音:這種方案的擴展性和效率是非常低的。
那如何進行優化呢?
首先看下 數據庫層面元數據擴展 ,常見的擴展方式是, 增加列 ,記錄更多的業務計數。
繼續答星球水友提問, 30WQPS的點贊計數業務,如何設計?
可以看到,這個 業務的特點 是:
(1) 吞吐量超高 ;
(2) 能夠接受一定數據不一致 ;
畫外音: 計數有微小不準確,不是大問題。
先用最樸素的思想, 只考慮點贊計數,可以怎麼做?
有幾點是最容易想到的:
(1)肯定 不能 用 數據庫抗 實時讀寫流量;
(2)redis天然支持固化, 可以用高可用redis集群來做固化存儲 ;
(3) 也可以用MySQL來做固化存儲 , redis做緩存 ,讀寫操作都落緩存, 異步線程定期刷DB ;
(4)架一層 計數服務 ,將計數與業務邏輯解耦;
此時 MySQL核心數據結構 是:
t_count(msg_id, praise_count)
此時 redis的KV設計 也不難:
key: msg_id
value: praise_count
似乎很容易就搞定了:
(1)服務可以水平擴展;
(2)數據量增加時,數據庫可以水平擴展;
(3)讀寫量增加時,緩存也可以水平擴展;
計數系統的 難點 ,還在於 業務擴展性 問題,以及 效率 問題。
以微博為例:
(1)用戶微博首頁, 有多條消息 list<msg_id> ,這是一種 擴展 ;
(2)同一條消息msg_id, 不止有點贊計數,還有閱讀計數,轉發計數,評論計數 ,這也是一種 擴展 ;
假如用最樸素的方式實現,多條消息多個計數的獲取偽代碼如下:
// (1) 獲取首頁所有消息 msg_id
list<msg_id> = getHomePageMsg(uid);
// (2)對於首頁的所有消息要 拉取多個計數
for( msg_id in list<msg_id>){
//(3.1)獲取 閱讀計數
getReadCount(msg_id);
//(3.2)獲取 轉發計數
getForwordCount(msg_id);
//(3.3)獲取 評論計數
getCommentCount(msg_id);
//(3.4)獲取 贊計數
getPraiseCount(msg_id);
}
由於同一個msg_id多了幾種業務計數, redis的key需要帶上業務flag ,升級為:
msg_id:read
msg_id:forword
msg_id:comment
msg_id:praise
用來區分共一個msg_id的四種不同業務計數,redis不能支持key的模糊操作,必須訪問四次reids。
假設首頁有100條消息, 這個方案 總結為:
(1)for循環每一條消息,100條消息100次;
(2)每條消息4次RPC獲取計數接口調用;
(3)每次調用服務要訪問reids,拼裝key獲取count;
畫外音:這種方案的擴展性和效率是非常低的。
那如何進行優化呢?
首先看下 數據庫層面元數據擴展 ,常見的擴展方式是, 增加列 ,記錄更多的業務計數。
如上圖所示,由一列點贊計數,擴充為四列閱讀、轉發、評論、點贊計數。
增加列這種業務計數擴展方式的 缺點 是: 每次要擴充業務計數時,總是需要修改表結構 ,增加列,很煩。
有沒有不需要變更表結構的擴展方式呢?
行擴展 是一種擴展性更好的方式。
繼續答星球水友提問, 30WQPS的點贊計數業務,如何設計?
可以看到,這個 業務的特點 是:
(1) 吞吐量超高 ;
(2) 能夠接受一定數據不一致 ;
畫外音: 計數有微小不準確,不是大問題。
先用最樸素的思想, 只考慮點贊計數,可以怎麼做?
有幾點是最容易想到的:
(1)肯定 不能 用 數據庫抗 實時讀寫流量;
(2)redis天然支持固化, 可以用高可用redis集群來做固化存儲 ;
(3) 也可以用MySQL來做固化存儲 , redis做緩存 ,讀寫操作都落緩存, 異步線程定期刷DB ;
(4)架一層 計數服務 ,將計數與業務邏輯解耦;
此時 MySQL核心數據結構 是:
t_count(msg_id, praise_count)
此時 redis的KV設計 也不難:
key: msg_id
value: praise_count
似乎很容易就搞定了:
(1)服務可以水平擴展;
(2)數據量增加時,數據庫可以水平擴展;
(3)讀寫量增加時,緩存也可以水平擴展;
計數系統的 難點 ,還在於 業務擴展性 問題,以及 效率 問題。
以微博為例:
(1)用戶微博首頁, 有多條消息 list<msg_id> ,這是一種 擴展 ;
(2)同一條消息msg_id, 不止有點贊計數,還有閱讀計數,轉發計數,評論計數 ,這也是一種 擴展 ;
假如用最樸素的方式實現,多條消息多個計數的獲取偽代碼如下:
// (1) 獲取首頁所有消息 msg_id
list<msg_id> = getHomePageMsg(uid);
// (2)對於首頁的所有消息要 拉取多個計數
for( msg_id in list<msg_id>){
//(3.1)獲取 閱讀計數
getReadCount(msg_id);
//(3.2)獲取 轉發計數
getForwordCount(msg_id);
//(3.3)獲取 評論計數
getCommentCount(msg_id);
//(3.4)獲取 贊計數
getPraiseCount(msg_id);
}
由於同一個msg_id多了幾種業務計數, redis的key需要帶上業務flag ,升級為:
msg_id:read
msg_id:forword
msg_id:comment
msg_id:praise
用來區分共一個msg_id的四種不同業務計數,redis不能支持key的模糊操作,必須訪問四次reids。
假設首頁有100條消息, 這個方案 總結為:
(1)for循環每一條消息,100條消息100次;
(2)每條消息4次RPC獲取計數接口調用;
(3)每次調用服務要訪問reids,拼裝key獲取count;
畫外音:這種方案的擴展性和效率是非常低的。
那如何進行優化呢?
首先看下 數據庫層面元數據擴展 ,常見的擴展方式是, 增加列 ,記錄更多的業務計數。
如上圖所示,由一列點贊計數,擴充為四列閱讀、轉發、評論、點贊計數。
增加列這種業務計數擴展方式的 缺點 是: 每次要擴充業務計數時,總是需要修改表結構 ,增加列,很煩。
有沒有不需要變更表結構的擴展方式呢?
行擴展 是一種擴展性更好的方式。
表結構固化為:
t_count(msg_id, count_key, count_value)
當要擴充業務計數時,增加一行就行,不需要修改表結構。
畫外音: 很多配置業務,會使用這種方案,方便增加配置。
增加行這種業務計數擴展方式的 缺點 是: 表數據行數會增加 ,但這不是主要矛盾,數據庫水平擴展能很輕鬆解決數據量大的問題。
接下來看下 redis批量獲取計數 的優化方案。
繼續答星球水友提問, 30WQPS的點贊計數業務,如何設計?
可以看到,這個 業務的特點 是:
(1) 吞吐量超高 ;
(2) 能夠接受一定數據不一致 ;
畫外音: 計數有微小不準確,不是大問題。
先用最樸素的思想, 只考慮點贊計數,可以怎麼做?
有幾點是最容易想到的:
(1)肯定 不能 用 數據庫抗 實時讀寫流量;
(2)redis天然支持固化, 可以用高可用redis集群來做固化存儲 ;
(3) 也可以用MySQL來做固化存儲 , redis做緩存 ,讀寫操作都落緩存, 異步線程定期刷DB ;
(4)架一層 計數服務 ,將計數與業務邏輯解耦;
此時 MySQL核心數據結構 是:
t_count(msg_id, praise_count)
此時 redis的KV設計 也不難:
key: msg_id
value: praise_count
似乎很容易就搞定了:
(1)服務可以水平擴展;
(2)數據量增加時,數據庫可以水平擴展;
(3)讀寫量增加時,緩存也可以水平擴展;
計數系統的 難點 ,還在於 業務擴展性 問題,以及 效率 問題。
以微博為例:
(1)用戶微博首頁, 有多條消息 list<msg_id> ,這是一種 擴展 ;
(2)同一條消息msg_id, 不止有點贊計數,還有閱讀計數,轉發計數,評論計數 ,這也是一種 擴展 ;
假如用最樸素的方式實現,多條消息多個計數的獲取偽代碼如下:
// (1) 獲取首頁所有消息 msg_id
list<msg_id> = getHomePageMsg(uid);
// (2)對於首頁的所有消息要 拉取多個計數
for( msg_id in list<msg_id>){
//(3.1)獲取 閱讀計數
getReadCount(msg_id);
//(3.2)獲取 轉發計數
getForwordCount(msg_id);
//(3.3)獲取 評論計數
getCommentCount(msg_id);
//(3.4)獲取 贊計數
getPraiseCount(msg_id);
}
由於同一個msg_id多了幾種業務計數, redis的key需要帶上業務flag ,升級為:
msg_id:read
msg_id:forword
msg_id:comment
msg_id:praise
用來區分共一個msg_id的四種不同業務計數,redis不能支持key的模糊操作,必須訪問四次reids。
假設首頁有100條消息, 這個方案 總結為:
(1)for循環每一條消息,100條消息100次;
(2)每條消息4次RPC獲取計數接口調用;
(3)每次調用服務要訪問reids,拼裝key獲取count;
畫外音:這種方案的擴展性和效率是非常低的。
那如何進行優化呢?
首先看下 數據庫層面元數據擴展 ,常見的擴展方式是, 增加列 ,記錄更多的業務計數。
如上圖所示,由一列點贊計數,擴充為四列閱讀、轉發、評論、點贊計數。
增加列這種業務計數擴展方式的 缺點 是: 每次要擴充業務計數時,總是需要修改表結構 ,增加列,很煩。
有沒有不需要變更表結構的擴展方式呢?
行擴展 是一種擴展性更好的方式。
表結構固化為:
t_count(msg_id, count_key, count_value)
當要擴充業務計數時,增加一行就行,不需要修改表結構。
畫外音: 很多配置業務,會使用這種方案,方便增加配置。
增加行這種業務計數擴展方式的 缺點 是: 表數據行數會增加 ,但這不是主要矛盾,數據庫水平擴展能很輕鬆解決數據量大的問題。
接下來看下 redis批量獲取計數 的優化方案。
原始方案,通過拼裝key來區分同一個msg_id的不同業務計數。
可以升級為,同一個value來存儲多個計數。
繼續答星球水友提問, 30WQPS的點贊計數業務,如何設計?
可以看到,這個 業務的特點 是:
(1) 吞吐量超高 ;
(2) 能夠接受一定數據不一致 ;
畫外音: 計數有微小不準確,不是大問題。
先用最樸素的思想, 只考慮點贊計數,可以怎麼做?
有幾點是最容易想到的:
(1)肯定 不能 用 數據庫抗 實時讀寫流量;
(2)redis天然支持固化, 可以用高可用redis集群來做固化存儲 ;
(3) 也可以用MySQL來做固化存儲 , redis做緩存 ,讀寫操作都落緩存, 異步線程定期刷DB ;
(4)架一層 計數服務 ,將計數與業務邏輯解耦;
此時 MySQL核心數據結構 是:
t_count(msg_id, praise_count)
此時 redis的KV設計 也不難:
key: msg_id
value: praise_count
似乎很容易就搞定了:
(1)服務可以水平擴展;
(2)數據量增加時,數據庫可以水平擴展;
(3)讀寫量增加時,緩存也可以水平擴展;
計數系統的 難點 ,還在於 業務擴展性 問題,以及 效率 問題。
以微博為例:
(1)用戶微博首頁, 有多條消息 list<msg_id> ,這是一種 擴展 ;
(2)同一條消息msg_id, 不止有點贊計數,還有閱讀計數,轉發計數,評論計數 ,這也是一種 擴展 ;
假如用最樸素的方式實現,多條消息多個計數的獲取偽代碼如下:
// (1) 獲取首頁所有消息 msg_id
list<msg_id> = getHomePageMsg(uid);
// (2)對於首頁的所有消息要 拉取多個計數
for( msg_id in list<msg_id>){
//(3.1)獲取 閱讀計數
getReadCount(msg_id);
//(3.2)獲取 轉發計數
getForwordCount(msg_id);
//(3.3)獲取 評論計數
getCommentCount(msg_id);
//(3.4)獲取 贊計數
getPraiseCount(msg_id);
}
由於同一個msg_id多了幾種業務計數, redis的key需要帶上業務flag ,升級為:
msg_id:read
msg_id:forword
msg_id:comment
msg_id:praise
用來區分共一個msg_id的四種不同業務計數,redis不能支持key的模糊操作,必須訪問四次reids。
假設首頁有100條消息, 這個方案 總結為:
(1)for循環每一條消息,100條消息100次;
(2)每條消息4次RPC獲取計數接口調用;
(3)每次調用服務要訪問reids,拼裝key獲取count;
畫外音:這種方案的擴展性和效率是非常低的。
那如何進行優化呢?
首先看下 數據庫層面元數據擴展 ,常見的擴展方式是, 增加列 ,記錄更多的業務計數。
如上圖所示,由一列點贊計數,擴充為四列閱讀、轉發、評論、點贊計數。
增加列這種業務計數擴展方式的 缺點 是: 每次要擴充業務計數時,總是需要修改表結構 ,增加列,很煩。
有沒有不需要變更表結構的擴展方式呢?
行擴展 是一種擴展性更好的方式。
表結構固化為:
t_count(msg_id, count_key, count_value)
當要擴充業務計數時,增加一行就行,不需要修改表結構。
畫外音: 很多配置業務,會使用這種方案,方便增加配置。
增加行這種業務計數擴展方式的 缺點 是: 表數據行數會增加 ,但這不是主要矛盾,數據庫水平擴展能很輕鬆解決數據量大的問題。
接下來看下 redis批量獲取計數 的優化方案。
原始方案,通過拼裝key來區分同一個msg_id的不同業務計數。
可以升級為,同一個value來存儲多個計數。
如上圖所示, 同一個msg_id的四個計數,存儲在一個value裡,從而避免多次redis訪問。
畫外音: 通過value來擴展,是不是很巧妙?
總結
計數業務,在數據量大,併發量大的時候,要考慮的一些技術點:
(1)用緩存抗讀寫;
(2)服務化,計數系統與業務系統解耦;
(3)水平切分擴展吞吐量、數據量、讀寫量;
(4)要考慮擴展性,數據庫層面常見的優化有:列擴展,行擴展兩種方式;
(5)要考慮批量操作,緩存層面常見的優化有:一個value存儲多個業務計數;
計數系統優化先聊到這裡,希望大家有收穫。
原文:http://www.tuicool.com/articles/VFNnmaj