PostgreSQL類微博FEED系統-設計與效能指標
標籤
PostgreSQL , feed , 微博 , 推送 , 分割槽 , 分片 , UDF , 挖掘 , 文字挖掘
背景
類微博系統,最頻繁用到的功能:
A,D,E使用者關注B使用者。
B使用者推送訊息。
A,D,E使用者接收訊息。
A,D,E使用者消費訊息。涉及消費排序演算法。
之前寫過一篇《三體高可用PCC大賽 – facebook微博 like場景 – 資料庫設計與效能壓測》
LIKE相關場景,用PostgreSQL來設計,效能槓槓的。
本文則是與訊息推送、訊息消費相關的場景。
以內容2048字為例。
設計
為了滿足高效率的推送與消費,設計時,需要考慮到分割槽。分割槽後,也便於將來做較為透明的分庫。
例如可以按使用者的UID進行雜湊分割槽。
1 hash 分割槽表
建立訊息推送表
create table tbl_feed(
uid int8, -- 使用者ID
from_uid int8, -- 被關注使用者ID
ts timestamp, -- 被關注使用者傳送該訊息的時間
content text, -- 被關注使用者傳送該訊息的內容
status int -- 訊息被當前使用者閱讀的狀態, 0 初始狀態, 1 已消費
);
建立partial index,因為消費時,只關心沒有被消費的記錄。
create index idx_tbl_feed_1 on tbl_feed(uid,ts) where status=0;
建立1024個分割槽
do language plpgsql $$
declare
begin
for i in 0..1023 loop
execute format(`create table tbl_feed_%s (like tbl_feed including all , constraint ck_tbl_feed_%s check(abs(mod(uid,1024))=%s)) inherits(tbl_feed)`, i, i, i);
end loop;
end;
$$;
2 寫入 UDF
目前RDS PG 10的分割槽表寫入效率和查詢效率不是特別理想,為了達到較好的寫入效率,建議可以先使用UDF,動態拼接SQL。
create or replace function ins_feed(int8, int8, timestamp, text, int) returns void as $$
declare
i int := abs(mod($1,1024)); -- 動態拼接表名
begin
execute format(`insert into tbl_feed_%s(uid,from_uid,ts,content,status) values(%s,%s,%L,%L,%s)`, i, $1,$2,$3,$4,$5);
end;
$$ language plpgsql strict;
寫入效能
假設有20億使用者,隨機輸入1個使用者,並推送一條2048個英文字的訊息。
PG 10,單例項,寫入 19.5 萬行/s,瓶頸主要在寫WAL日誌的LOCK上。
set uid random(1,2000000000)
select ins_feed(:uid,:uid+1,now()::timestamp,repeat(md5(`a`),64),0);
pgbench -M prepared -n -r -P 1 -f ./test.sql -c 56 -j 56 -T 120
transaction type: ./test.sql
scaling factor: 1
query mode: prepared
number of clients: 56
number of threads: 56
duration: 120 s
number of transactions actually processed: 23464891
latency average = 0.286 ms
latency stddev = 0.486 ms
tps = 195379.681306 (including connections establishing)
tps = 195404.169885 (excluding connections establishing)
statement latencies in milliseconds:
0.001 set uid random(1,2000000000)
0.285 select ins_feed(:uid,:uid+1,now()::timestamp,repeat(md5(`a`),64),0);
消費 UDF
目前RDS PG 10的分割槽表寫入效率和查詢效率不是特別理想,為了達到較好的寫入效率,建議可以先使用UDF,動態拼接SQL。
create or replace function get_feed(int8, int, text) returns setof tbl_feed as $$
declare
i int := abs(mod($1,1024)); -- 動態拼接表名
begin
return query execute format(`with tmp as
(
update tbl_feed_%s set status=1 where ctid = any (array(
select ctid from tbl_feed_%s where status=0 and uid=%s order by ts limit %s -- 每次消費N條,按時間先或後消費都可以,都會走索引
))
returning *
)
select * from tmp order by %s`, -- 排序演算法可以寫成UDF,或引數傳入, 本例使用ts排序
i, i, $1, $2, $3
);
end;
$$ language plpgsql strict;
消費例子
postgres=# select * from get_feed(642960384,10,`from_uid`);
-[ RECORD 1 ]------------------------------------------------------------------------------------------------
uid | 642960384
from_uid | 642960385
ts | 2018-03-05 19:41:40.574568
content | 0cc175b9c0f1b6a831c399e2697726610cc175b9c0f1b6a831c399e2697726610cc175b9c0f1b6a831c399e2697726610cc17
9c0f1b6a831c399e2697726610cc175b9c0f1b6a831c399e2697726610cc175b9c0f1b6a831c399e2697726610cc175b9c0f1b6a831c399e
2697726610cc175b9c0f1b6a831c399e2697726610cc175b9c0f1b6a831c399e2697726610cc175b9c0f1b6a831c399e2697726610cc175b
9c0f1b6a831c399e2697726610cc175b9c0f1b6a831c399e2697726610cc175b9c0f1b6a831c399e2697726610cc175b9c0f1b6a831c399e
2697726610cc175b9c0f1b6a831c399e2697726610cc175b9c0f1b6a831c399e2697726610cc175b9c0f1b6a831c399e2697726610cc175b
9c0f1b6a831c399e2697726610cc175b9c0f1b6a831c399e2697726610cc175b9c0f1b6a831c399e2697726610cc175b9c0f1b6a831c399e
2697726610cc175b9c0f1b6a831c399e2697726610cc175b9c0f1b6a831c399e2697726610cc175b9c0f1b6a831c399e2697726610cc175b
9c0f1b6a831c399e2697726610cc175b9c0f1b6a831c399e2697726610cc175b9c0f1b6a831c399e2697726610cc175b9c0f1b6a831c399e
2697726610cc175b9c0f1b6a831c399e2697726610cc175b9c0f1b6a831c399e2697726610cc175b9c0f1b6a831c399e2697726610cc175b
9c0f1b6a831c399e2697726610cc175b9c0f1b6a831c399e2697726610cc175b9c0f1b6a831c399e2697726610cc175b9c0f1b6a831c399e
2697726610cc175b9c0f1b6a831c399e2697726610cc175b9c0f1b6a831c399e2697726610cc175b9c0f1b6a831c399e2697726610cc175b
9c0f1b6a831c399e2697726610cc175b9c0f1b6a831c399e2697726610cc175b9c0f1b6a831c399e2697726610cc175b9c0f1b6a831c399e
2697726610cc175b9c0f1b6a831c399e2697726610cc175b9c0f1b6a831c399e2697726610cc175b9c0f1b6a831c399e2697726610cc175b
9c0f1b6a831c399e2697726610cc175b9c0f1b6a831c399e2697726610cc175b9c0f1b6a831c399e2697726610cc175b9c0f1b6a831c399e
2697726610cc175b9c0f1b6a831c399e2697726610cc175b9c0f1b6a831c399e2697726610cc175b9c0f1b6a831c399e2697726610cc175b
9c0f1b6a831c399e2697726610cc175b9c0f1b6a831c399e2697726610cc175b9c0f1b6a831c399e2697726610cc175b9c0f1b6a831c399e
2697726610cc175b9c0f1b6a831c399e2697726610cc175b9c0f1b6a831c399e2697726610cc175b9c0f1b6a831c399e2697726610cc175b
9c0f1b6a831c399e2697726610cc175b9c0f1b6a831c399e2697726610cc175b9c0f1b6a831c399e2697726610cc175b9c0f1b6a831c399e
2697726610cc175b9c0f1b6a831c399e269772661
status | 1
消費效能
為了觀察到實際的消費,即每次消費都有至少20條被真實消費掉,這裡先生成一批密集的資料再測。
set uid random(1,4096)
select ins_feed(:uid,:uid+1,now()::timestamp,repeat(md5(`a`),64),0);
pgbench -M prepared -n -r -P 1 -f ./test.sql -c 56 -j 56 -T 120
隨機輸入一個隨機使用者,每次消費20行。平均每秒消費 2.7 萬次。
# set uid random(1,2000000000)
測試時使用 set uid random(1,4096)
select * from get_feed(:uid,20,`ts`);
pgbench -M prepared -n -r -P 1 -f ./test.sql -c 56 -j 56 -T 45
transaction type: ./test.sql
scaling factor: 1
query mode: prepared
number of clients: 56
number of threads: 56
duration: 45 s
number of transactions actually processed: 1195840
latency average = 2.106 ms
latency stddev = 2.707 ms
tps = 26560.345111 (including connections establishing)
tps = 26572.467067 (excluding connections establishing)
statement latencies in milliseconds:
0.001 set uid random(1,4096)
2.105 select * from get_feed(:uid,20,`ts`);
小結
PG 11後,分割槽表的寫入、查詢效率會大幅提升。將來可以直接使用分割槽表,避免使用UDF動態SQL來訪問分割槽。
PostgreSQL內建UDF(plpgsql, plpython, pljava, plv8 等)功能,可以支援任意排序演算法的擴充套件。
單例項效能指標:
推送 | 消費
19.5 萬行/s | 54 萬行/s , 2.7 萬次/s (平均每次消費20行)
其他輔助技術
1、partial index,只對關心的資料建立索引。
2、流計算,結合流計算實現實時統計,實時資料轉換,實時歸納、清洗等。
3、brin 索引,對TS欄位,可以使用時序索引。索引小,效能好。類似業務:
《PostgreSQL 時序最佳實踐 – 證券交易系統資料庫設計 – 阿里雲RDS PostgreSQL最佳實踐》
4、plproxy,實現分片。阿里雲將會提供類似DRDS的PG中介軟體服務,使得PG的分庫分表透明化。
《阿里雲ApsaraDB RDS for PostgreSQL 最佳實踐 – 4 水平分庫(plproxy) 之 節點擴充套件》
《阿里雲ApsaraDB RDS for PostgreSQL 最佳實踐 – 3 水平分庫(plproxy) vs 單機 效能》
《阿里雲ApsaraDB RDS for PostgreSQL 最佳實踐 – 2 教你RDS PG的水平分庫(plproxy)》
5、gpdb mpp,將來如果需要對FEED資料進行挖掘,可以使用HDB PG(Greenplum)。MPP架構,OLAP效能非常棒。類似案例:
《打造雲端流計算、線上業務、資料分析的業務資料閉環 – 阿里雲RDS、HybridDB for PostgreSQL最佳實踐》
6、gin 倒排,文字搜尋。實現文字全文檢索。
《PostgreSQL 全文檢索之 – 位置匹配 過濾語法(例如 `速度 <1> 激情`)》
《PostgreSQL – 全文檢索內建及自定義ranking演算法介紹 與案例》
《用PostgreSQL 做實時高效 搜尋引擎 – 全文檢索、模糊查詢、正則查詢、相似查詢、ADHOC查詢》
7、海明,rum,pg_trgm,文字相似搜尋
《海量資料,海明(simhash)距離高效檢索(smlar) – 阿里雲RDS PosgreSQL最佳實踐》
《17種文字相似演算法與GIN索引 – pg_similarity》
《PostgreSQL結合餘弦、線性相關演算法 在文字、圖片、陣列相似 等領域的應用 – 3 rum, smlar應用場景分析》
《PostgreSQL結合餘弦、線性相關演算法 在文字、圖片、陣列相似 等領域的應用 – 2 smlar外掛詳解》
《聊一聊雙十一背後的技術 – 毫秒分詞算啥, 試試正則和相似度》
《PostgreSQL 文字資料分析實踐之 – 相似度分析》
8、plpython, madlib, 文字挖掘
《[轉載]易上手的資料探勘、視覺化與機器學習工具: Orange介紹》
9、PPC大賽
《三體高可用PCC大賽 – facebook微博 like場景 – 資料庫設計與效能壓測》
《阿里雲 PostgreSQL 產品生態;案例、開發實踐、管理實踐、學習資料、學習視訊》
相關文章
- Linux作業系統效能指標監控與通知Linux作業系統指標
- 仿微博訊息中心的系統設計與實現
- 如何設計一個微博系統?- 4招教你搞定系統設計
- Go之獲取系統效能指標 - goPsutilGo指標
- Feed 流系統雜談
- AIX 作業系統調優 效能指標祥解AI作業系統指標
- 效能指標指標
- PostgreSQL構建通用標籤系統SQL
- 蘇州能源計量檢測與能效統計管理系統
- 昆蟲分類與檢索系統的設計與開發
- 【網路】效能指標與測試工具指標
- 如何打造千萬級Feed流系統
- 【系統設計】指標監控和告警系統指標
- feed服務專案設計思考
- 計算機網路的效能指標計算機網路指標
- 前端頁面效能指標與採集方式前端指標
- 圖書管理系統設計類圖
- Feed流系統重構-架構篇架構
- Java程式設計師必須掌握的7個Java效能指標!Java程式設計師指標
- 微博feed系統的推(push)模式和拉(pull)模式和時間分割槽拉模式架構探討模式架構
- MySQL常用效能指標MySql指標
- 目標控制器數字孿生系統的研究與設計
- 設計模式與系統階段設計模式
- 需求改進與系統設計
- 控制指標與統計指標指標
- 系統設計與普通設計思考的區別
- 計量系統的設計與實施
- Loadrunner效能指標分析指標
- 微機原理與系統設計筆記6 | 儲存器系統設計筆記
- 如何進行系統分析與設計
- PDM與ERP系統整合設計
- 應用系統規劃與設計
- 計算機網路 -- 計算機網路的效能指標計算機網路指標
- PostgreSQL pgbackrest 怎麼設計帶遠端控制端的PG備份系統系統SQLREST
- 類Excel設計器的基本標準Excel
- PLC的7大效能指標指標
- 軟體中的效能指標指標
- Linux 網路效能指標Linux指標