'大型社交網站每秒30W次的點贊業務,怎麼優化?'

"
"
大型社交網站每秒30W次的點贊業務,怎麼優化?

繼續答星球水友提問, 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

"
大型社交網站每秒30W次的點贊業務,怎麼優化?

繼續答星球水友提問, 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

大型社交網站每秒30W次的點贊業務,怎麼優化?

似乎很容易就搞定了:

(1)服務可以水平擴展;

(2)數據量增加時,數據庫可以水平擴展;

(3)讀寫量增加時,緩存也可以水平擴展;

計數系統的 難點 ,還在於 業務擴展性 問題,以及 效率 問題。

以微博為例:

"
大型社交網站每秒30W次的點贊業務,怎麼優化?

繼續答星球水友提問, 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

大型社交網站每秒30W次的點贊業務,怎麼優化?

似乎很容易就搞定了:

(1)服務可以水平擴展;

(2)數據量增加時,數據庫可以水平擴展;

(3)讀寫量增加時,緩存也可以水平擴展;

計數系統的 難點 ,還在於 業務擴展性 問題,以及 效率 問題。

以微博為例:

大型社交網站每秒30W次的點贊業務,怎麼優化?

(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;

畫外音:這種方案的擴展性和效率是非常低的。

那如何進行優化呢?

首先看下 數據庫層面元數據擴展 ,常見的擴展方式是, 增加列 ,記錄更多的業務計數。

"
大型社交網站每秒30W次的點贊業務,怎麼優化?

繼續答星球水友提問, 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

大型社交網站每秒30W次的點贊業務,怎麼優化?

似乎很容易就搞定了:

(1)服務可以水平擴展;

(2)數據量增加時,數據庫可以水平擴展;

(3)讀寫量增加時,緩存也可以水平擴展;

計數系統的 難點 ,還在於 業務擴展性 問題,以及 效率 問題。

以微博為例:

大型社交網站每秒30W次的點贊業務,怎麼優化?

(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;

畫外音:這種方案的擴展性和效率是非常低的。

那如何進行優化呢?

首先看下 數據庫層面元數據擴展 ,常見的擴展方式是, 增加列 ,記錄更多的業務計數。

大型社交網站每秒30W次的點贊業務,怎麼優化?

如上圖所示,由一列點贊計數,擴充為四列閱讀、轉發、評論、點贊計數。

增加列這種業務計數擴展方式的 缺點 是: 每次要擴充業務計數時,總是需要修改表結構 ,增加列,很煩。

有沒有不需要變更表結構的擴展方式呢?

行擴展 是一種擴展性更好的方式。

"
大型社交網站每秒30W次的點贊業務,怎麼優化?

繼續答星球水友提問, 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

大型社交網站每秒30W次的點贊業務,怎麼優化?

似乎很容易就搞定了:

(1)服務可以水平擴展;

(2)數據量增加時,數據庫可以水平擴展;

(3)讀寫量增加時,緩存也可以水平擴展;

計數系統的 難點 ,還在於 業務擴展性 問題,以及 效率 問題。

以微博為例:

大型社交網站每秒30W次的點贊業務,怎麼優化?

(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;

畫外音:這種方案的擴展性和效率是非常低的。

那如何進行優化呢?

首先看下 數據庫層面元數據擴展 ,常見的擴展方式是, 增加列 ,記錄更多的業務計數。

大型社交網站每秒30W次的點贊業務,怎麼優化?

如上圖所示,由一列點贊計數,擴充為四列閱讀、轉發、評論、點贊計數。

增加列這種業務計數擴展方式的 缺點 是: 每次要擴充業務計數時,總是需要修改表結構 ,增加列,很煩。

有沒有不需要變更表結構的擴展方式呢?

行擴展 是一種擴展性更好的方式。

大型社交網站每秒30W次的點贊業務,怎麼優化?

表結構固化為:

t_count(msg_id, count_key, count_value)

當要擴充業務計數時,增加一行就行,不需要修改表結構。

畫外音: 很多配置業務,會使用這種方案,方便增加配置。

增加行這種業務計數擴展方式的 缺點 是: 表數據行數會增加 ,但這不是主要矛盾,數據庫水平擴展能很輕鬆解決數據量大的問題。

接下來看下 redis批量獲取計數 的優化方案。

"
大型社交網站每秒30W次的點贊業務,怎麼優化?

繼續答星球水友提問, 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

大型社交網站每秒30W次的點贊業務,怎麼優化?

似乎很容易就搞定了:

(1)服務可以水平擴展;

(2)數據量增加時,數據庫可以水平擴展;

(3)讀寫量增加時,緩存也可以水平擴展;

計數系統的 難點 ,還在於 業務擴展性 問題,以及 效率 問題。

以微博為例:

大型社交網站每秒30W次的點贊業務,怎麼優化?

(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;

畫外音:這種方案的擴展性和效率是非常低的。

那如何進行優化呢?

首先看下 數據庫層面元數據擴展 ,常見的擴展方式是, 增加列 ,記錄更多的業務計數。

大型社交網站每秒30W次的點贊業務,怎麼優化?

如上圖所示,由一列點贊計數,擴充為四列閱讀、轉發、評論、點贊計數。

增加列這種業務計數擴展方式的 缺點 是: 每次要擴充業務計數時,總是需要修改表結構 ,增加列,很煩。

有沒有不需要變更表結構的擴展方式呢?

行擴展 是一種擴展性更好的方式。

大型社交網站每秒30W次的點贊業務,怎麼優化?

表結構固化為:

t_count(msg_id, count_key, count_value)

當要擴充業務計數時,增加一行就行,不需要修改表結構。

畫外音: 很多配置業務,會使用這種方案,方便增加配置。

增加行這種業務計數擴展方式的 缺點 是: 表數據行數會增加 ,但這不是主要矛盾,數據庫水平擴展能很輕鬆解決數據量大的問題。

接下來看下 redis批量獲取計數 的優化方案。

大型社交網站每秒30W次的點贊業務,怎麼優化?

原始方案,通過拼裝key來區分同一個msg_id的不同業務計數。

可以升級為,同一個value來存儲多個計數。

"
大型社交網站每秒30W次的點贊業務,怎麼優化?

繼續答星球水友提問, 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

大型社交網站每秒30W次的點贊業務,怎麼優化?

似乎很容易就搞定了:

(1)服務可以水平擴展;

(2)數據量增加時,數據庫可以水平擴展;

(3)讀寫量增加時,緩存也可以水平擴展;

計數系統的 難點 ,還在於 業務擴展性 問題,以及 效率 問題。

以微博為例:

大型社交網站每秒30W次的點贊業務,怎麼優化?

(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;

畫外音:這種方案的擴展性和效率是非常低的。

那如何進行優化呢?

首先看下 數據庫層面元數據擴展 ,常見的擴展方式是, 增加列 ,記錄更多的業務計數。

大型社交網站每秒30W次的點贊業務,怎麼優化?

如上圖所示,由一列點贊計數,擴充為四列閱讀、轉發、評論、點贊計數。

增加列這種業務計數擴展方式的 缺點 是: 每次要擴充業務計數時,總是需要修改表結構 ,增加列,很煩。

有沒有不需要變更表結構的擴展方式呢?

行擴展 是一種擴展性更好的方式。

大型社交網站每秒30W次的點贊業務,怎麼優化?

表結構固化為:

t_count(msg_id, count_key, count_value)

當要擴充業務計數時,增加一行就行,不需要修改表結構。

畫外音: 很多配置業務,會使用這種方案,方便增加配置。

增加行這種業務計數擴展方式的 缺點 是: 表數據行數會增加 ,但這不是主要矛盾,數據庫水平擴展能很輕鬆解決數據量大的問題。

接下來看下 redis批量獲取計數 的優化方案。

大型社交網站每秒30W次的點贊業務,怎麼優化?

原始方案,通過拼裝key來區分同一個msg_id的不同業務計數。

可以升級為,同一個value來存儲多個計數。

大型社交網站每秒30W次的點贊業務,怎麼優化?

如上圖所示, 同一個msg_id的四個計數,存儲在一個value裡,從而避免多次redis訪問。

畫外音: 通過value來擴展,是不是很巧妙?

總結

計數業務,在數據量大,併發量大的時候,要考慮的一些技術點:

(1)用緩存抗讀寫;

(2)服務化,計數系統與業務系統解耦;

(3)水平切分擴展吞吐量、數據量、讀寫量;

(4)要考慮擴展性,數據庫層面常見的優化有:列擴展,行擴展兩種方式;

(5)要考慮批量操作,緩存層面常見的優化有:一個value存儲多個業務計數;

計數系統優化先聊到這裡,希望大家有收穫。

原文:http://www.tuicool.com/articles/VFNnmaj

"

相關推薦

推薦中...