閱讀187 返回首頁    go 阿裏雲 go 技術社區[雲棲]


音視圖(泛內容)網站透視分析 DB設計 - 阿裏雲(RDS、HybridDB) for PostgreSQL最佳實踐

標簽

PostgreSQL , 用戶透視 , 設備透視 , 圈人 , 標簽 , 視頻網站 , 優酷 , 土豆 , 喜馬拉雅


背景

日常生活中,人們使用最多的除了社交類網站、購物網站,估計就是音頻、視頻、圖文信息類內容網站了。

視頻網站,已經滲透到各種終端,除了喜聞樂見的手機,還包括移動終端、電腦、盒子、電視、投影儀等。有設備屬性、會員屬性、渠道屬性等。

內容運營是非常重要的環節,而透視則是運營的重要武器。

業務需求

1、生成設備、會員畫像

ID、各個維度的標簽、其中包括一些多值列標簽(例如最近7天、一個月看過的導演、演員主演的片子,。。。。。。)

通常多值列的值可能是幾十萬的量(幾十萬演員,幾十萬片源,。。。)。通常一個設備、人有幾十上百個屬性列。通常一個設備單個多值列內可能有幾十個VALUE。

2、畫像透視

2.1、根據任意標簽條件組合查詢滿足條件的數目(圈中設備、會員的數目,count)

2.2、根據任意標簽條件組合圈選出人群,統計該人群某些列中各種的分別數目比例(count, group by, 分位數, 多維透視)

並發要求較低。

多維透視和分位數統計詳見:

《PostgreSQL 9.5 new feature - Support GROUPING SETS, CUBE and ROLLUP.》

《PostgreSQL aggregate function 2 : Aggregate Functions for Statistics》

3、圈選目標設備、會員

根據任意標簽條件組合分頁查詢滿足條件的id列表(圈出滿足條件的設備或會員)

並發要求較低。

4、點查(鍵值查詢)需求(根據任意標簽條件組合和用戶id查詢這個用戶是否滿足該圈人規則)。

點查的並發需求較高,可能達到幾萬次請求每秒。

小結,量級估算:

作為一個音頻視頻網站,內容數目通常不會超過百萬(當然,引入了小視頻、自媒體後,也許有可能達到億甚至百億級別)。

用戶數+設備數頂到天也就百億(地球人口才多少呀)。而且設備是會老化的,活躍設備數,估計頂天也就十億級別。

標簽列數,這個看抽象能力,就以百來計吧。多值列(例如喜歡的演員、影片、導演等)的占比可能較多,預計一半一半吧。

多值列的VALUE範圍(例如演員、影片、導演等),預計在百萬級別。(A用戶喜愛綜藝明星標簽:汪涵,張宇,劉維)

多值列標簽,預計在幾十個的級別,通常對運營有效的是最近看過的影片。除了鑒黃師,一般也不會一天到晚的看電影對吧。

G點分析

1、多值列的存儲和透視。

1.1 多值列的存儲:

PostgreSQL支持數組類型,可以作為多值標簽列的數據類型。同時由於它是一個數據類型,所以內置了數組常見的包含、相交、不相交、疊加、UNNEST、類型轉換的操作符和函數。使用起來非常方便。

https://www.postgresql.org/docs/9.6/static/functions-array.html

1.2 多值列的透視:

例如用戶喜愛的演員是個多值列,求浙江省的用戶最喜愛的演員TOP 10。

表級統計,可以直接使用統計信息進行估計:

《PostgreSQL pg_stats used to estimate top N freps values and explain rows》

如果是帶條件的TOP N,需要實際的運算,unnest(array)即可,例如

select unnest(c2) as c2, count(*) from   -- 使用unnest將數組元素解開進行統計  
  tbl  
group by 1  
order by 2 desc   
limit 10;       -- 求c2多值列,在某個條件下的TOP 10元素  

2、時間、空間(地理位置)維度分析

會員、設備實際上是有地理位置屬性的,我們可以加入GIS透視。PostgreSQL支持geometry類型、索引、操作符、聚合函數、窗口函數、聚類分析函數等。在GIS方麵的處理能力特別強悍。

有許多案例可以參考:

《通過空間思想理解GiST索引的構造》

《Greenplum 空間(GIS)數據檢索 b-tree & GiST 索引實踐 - 阿裏雲HybridDB for PostgreSQL最佳實踐》

《數據尋龍點穴(空間聚集分析) - 阿裏雲RDS PostgreSQL最佳實踐》

《PostGIS空間索引(GiST、BRIN、R-Tree)選擇、優化 - 阿裏雲RDS PostgreSQL最佳實踐》

《PostGIS 空間數據學習建議》

《(新零售)商戶網格化(基於位置GIS)運營 - 阿裏雲RDS PostgreSQL、HybridDB for PostgreSQL最佳實踐》

《時間、空間、對象多維屬性 海量數據任意多維 高效檢索 - 阿裏雲RDS PostgreSQL最佳實踐》

《SRID (空間引用識別號, 坐標係)》

《PostgreSQL\GPDB 毫秒級海量時空數據透視 典型案例分享》

《PostgreSQL\GPDB 毫秒級海量多維數據透視 案例分享》

《空間複合索引加速空間搜索》

《自動選擇正確索引訪問接口(btree,hash,gin,gist,sp-gist,brin,bitmap...)的方法》

《無人駕駛背後的技術 - PostGIS點雲(pointcloud)應用 - 2》

《無人駕駛背後的技術 - PostGIS點雲(pointcloud)應用 - 1》

《geohash vs PostGIS》

《奔跑吧,大屏 - 時間+空間 實時四維數據透視》

《視覺挖掘與PostGIS空間數據庫的完美邂逅 - 廣告營銷\圈人》

《PostGIS 多點幾何類型 空字符構造異常CASE》

《開放地圖OpenStreetMap與PostGIS的三生三世十裏桃花》

《基於PostgreSQL和地理位置信息打造的洞察平台 - CARTO》

《PostGIS 地理信息、柵格數據 多核並行處理(st_memunion, st_union)》

《蜂巢的藝術與技術價值 - PostgreSQL PostGIS's hex-grid》

《(AR虛擬現實)紅包 技術思考 - GIS與圖像識別的完美結合》

《如何建立GIS測試環境 - 將openstreetmap的樣本數據導入PostgreSQL PostGIS庫》

《PostgreSQL 最佳實踐 - 塊級增量備份(ZFS篇)單個數據庫采用多個zfs卷(如表空間)時如何一致性備份》

《PostgreSQL 百億地理位置數據 近鄰查詢性能》

《GIS附近查找性能優化 - PostGIS long lat geometry distance search tuning using gist knn function》

《PostgreSQL 9.1 nearest-neighbor search use gist index》

3、運算量

透視需求,需要較大運算能力。PostgreSQL 9.6開始支持多核並行,以目前的10萬左右的硬件,單機每秒可以處理的吞吐約10GB/s,已經達到了HTAP的水準。

如果要尋求更好的分析計算能力,可以采用阿裏雲HybridDB for PostgreSQL,屬於PostgreSQL的多機並行(OLAP)版本。支持列存儲、行存儲、壓縮、多機並行等特性。

4、數據導入

通常視頻係統的用戶畫像實時性要求並不高,每天都會生成新的用戶畫像,每天將生成的畫像寫入阿裏雲RDS PostgreSQL或HybridDB for PostgreSQL,提供透視即可。

架構設計

為了同時滿足透視、高並發點查的需求。有兩套方案可以實現。

方案1:阿裏雲 RDS PG 9.4 + HDB PG

pic

通過ETL調度係統,畫像數據經過OSS導入RDS PG和HDB PG。

點查需求交給RDS PG 9.4。

透視需求交給HDB PG。

方案2:阿裏雲 RDS PG 10

pic

RDS PG 10,支持多核並行,64核的機器每秒處理的數據約10GB(硬盤的帶寬視情況,通常單塊NVME卡可以達到約1.6GB/s)。很容易估算透視的時間。

如果業務的透視需求時間允許,可以考慮隻使用RDS PG 10來實現業務的目標。

另一方麵,RDS PG 10支持多個索引BITMAP SCAN合並掃描,實際上不需要掃描全表。效率非常高。

《PostgreSQL bitmapAnd, bitmapOr, bitmap index scan, bitmap heap scan》

《多字段,任意組合條件查詢(無需建模) - 毫秒級實時圈人 最佳實踐》

DEMO 1(方案1 驗證與性能)

產生隨機多值列的函數

創建一個函數,方便產生我們需要的多值列。

create or replace function gen_rand_int(  
  int,  -- 類別數量:影片、演員、分類(喜劇、恐怖...)、年份、種類(電影、電視劇...).... 。  
  int,  -- 熱門類別數量,類似大V數。必須小於$1  
  int   -- 標簽數  
) returns int[] as $$  
  select array(  
    select (ceil(random()*$1))::int+$2 from generate_series(1,$3/2)  -- 一半普通  
    union all   
    select (ceil(random()*$2))::int from generate_series(1,$3/2)     -- 一半熱門  
  ) ;  
$$ language sql strict;  
  
postgres=# select gen_rand_int(10000,100,30);  
                                                      gen_rand_int                                                         
-------------------------------------------------------------------------------------------------------------------------  
 {5946,6877,7287,9091,1761,4870,2100,573,8388,2079,1400,9744,703,4834,6843,18,13,32,97,53,68,43,72,32,62,85,47,15,79,70}  
(1 row)  
  
postgres=# select gen_rand_int(10000,100,30);  
                                                       gen_rand_int                                                         
--------------------------------------------------------------------------------------------------------------------------  
 {4558,3818,3836,1744,2808,5496,6513,4808,5969,4801,6786,7268,4444,7388,2002,11,91,32,43,88,85,11,30,56,43,92,40,90,7,19}  
(1 row)  
  
postgres=# select gen_rand_int(10000,100,30) from generate_series(1,10);  
                                                       gen_rand_int                                                          
---------------------------------------------------------------------------------------------------------------------------  
 {5950,6793,6047,7559,4635,1463,8022,1337,9059,8822,1641,6099,116,6626,5311,77,89,62,30,72,48,56,29,72,72,61,29,64,60,38}  
 {9932,1943,579,5878,9401,5113,7241,7322,6349,6200,6043,7889,2199,6059,4415,74,37,32,35,66,3,83,22,31,54,93,91,83,56,51}  
 {2109,5528,7033,2587,1306,6333,7600,8446,3555,3848,4546,9498,1636,6644,5456,59,39,90,90,74,56,93,56,77,24,10,70,15,92,26}  
 {6594,1261,8079,3526,3748,9284,9759,1247,7630,3213,4995,2075,2610,6531,8619,79,23,24,69,13,97,24,5,53,1,28,62,70,42,54}  
 {9646,769,6562,7625,4195,210,6808,3853,1356,4337,6966,6251,6312,9476,2681,48,73,49,72,41,62,68,65,66,21,65,94,82,35,36}  
 {3558,3132,4296,10019,657,8391,128,7364,2143,1384,1601,9008,7534,7813,8383,1,25,56,49,96,97,10,64,61,76,84,26,70,65,61}  
 {543,10050,9165,4739,9969,9721,3029,9997,6985,5071,1280,8486,3979,8714,6198,22,87,86,77,36,81,73,45,45,34,21,28,59,90,93}  
 {2024,9511,9292,1089,4149,9160,710,7078,9056,7595,2048,236,5980,5927,8850,20,80,74,6,57,9,87,30,54,31,64,75,58,22,64}  
 {5106,4223,5900,4297,5211,9949,3357,5821,6926,2313,3315,8874,2449,9195,4701,11,11,26,85,16,83,94,2,13,48,33,76,22,90,98}  
 {8637,4072,3953,4436,8268,9064,4285,1525,4784,1110,3737,7999,9884,6086,7093,44,71,81,70,56,97,53,50,99,65,97,31,40,18,21}  
(10 rows)  

畫像表

1、阿裏雲 RDS PostgreSQL

-- 70個字段  
-- 40個INT字段,10000,1000,100取值範圍。  
-- 30個數組字段,使用上述隨機數組生成函數(總標簽數10萬,熱門標簽150,關注標簽數20)  
  
-- PostgreSQL 10  
do language plpgsql $$  
declare  
  sql text := '';  
begin  
  for i in 1..10 loop  
    sql := sql||'c'||i||' int default random()*10000,';  
  end loop;  
  for i in 11..20 loop  
    sql := sql||'c'||i||' int default random()*1000,';  
  end loop;  
  for i in 21..40 loop  
    sql := sql||'c'||i||' int default random()*100,';  
  end loop;  
  for i in 41..70 loop  
    sql := sql||'c'||i||' int[] default gen_rand_int(100000,150,20),';  
  end loop;  
  sql := rtrim(sql, ',');  
  sql := 'create table test ('||sql||') with (autovacuum_enabled=off, toast.autovacuum_enabled=off, parallel_workers=32)';  
  execute sql;  
end;  
$$;  
  
/*   
  
-- 建立分區表  
  
-- PostgreSQL 10  
do language plpgsql $$  
declare  
begin  
  for i in 1..64 loop  
    execute 'create unlogged table test'||i||'(like test including all) inherits(test) with (autovacuum_enabled=off, toast.autovacuum_enabled=off, parallel_workers=32)';  
  end loop;  
end;  
$$;  
  
*/  

2、阿裏雲HybridDB for PostgreSQL

-- Greenplum  
  
create or replace function cb() returns void as   
$$  
declare  
  sql text := '';  
begin  
  for i in 1..10 loop  
    sql := sql||'c'||i||' int default random()*10000,';  
  end loop;  
  for i in 11..20 loop  
    sql := sql||'c'||i||' int default random()*1000,';  
  end loop;  
  for i in 21..40 loop  
    sql := sql||'c'||i||' int default random()*100,';  
  end loop;  
  for i in 41..70 loop  
    sql := sql||'c'||i||' int[] default gen_rand_int(100000,150,20),';  
  end loop;  
  sql := rtrim(sql, ',');  
  sql := 'create table test ('||sql||') with (APPENDONLY=true, ORIENTATION=column, COMPRESSTYPE=zlib, CHECKSUM=false, COMPRESSLEVEL=5) distributed randomly';  
  execute sql;  
end;  
$$ language plpgsql strict;  
  
select cb();  

數據導入

1、寫入示例

insert into test values (1);  
  
postgres=# select * from test;  
-[ RECORD 1 ]-------------------------------------------------------------------------------------------  
c1  | 1  
c2  | 4880  
c3  | 6058  
c4  | 1080  
c5  | 9862  
c6  | 7605  
c7  | 9139  
c8  | 2541  
c9  | 5599  
c10 | 9818  
c11 | 658  
c12 | 882  
c13 | 249  
c14 | 514  
c15 | 504  
c16 | 390  
c17 | 97  
c18 | 422  
c19 | 906  
c20 | 374  
c21 | 25  
c22 | 4  
c23 | 74  
c24 | 87  
c25 | 52  
c26 | 56  
c27 | 83  
c28 | 47  
c29 | 36  
c30 | 18  
c31 | 40  
c32 | 29  
c33 | 67  
c34 | 1  
c35 | 40  
c36 | 66  
c37 | 77  
c38 | 31  
c39 | 91  
c40 | 33  
c41 | {29495,57121,21227,54417,8477,71587,93375,18150,13788,84006,84,58,133,45,38,62,128,12,133,32}  
c42 | {26442,28622,50446,93545,29479,90221,59274,6384,21385,50413,59,76,11,91,8,24,48,148,51,68}  
c43 | {82075,89069,83949,70379,18540,9073,11113,3676,17058,99304,38,65,42,113,55,86,98,144,95,130}  
c44 | {46129,2464,37175,53362,62667,42021,68922,94306,40090,2482,60,33,137,35,139,15,49,5,20,74}  
c45 | {2359,38160,92410,29984,13302,29003,86937,78513,24719,50124,98,106,79,3,36,23,66,139,14,126}  
c46 | {95012,48541,5658,86261,71773,97751,95901,3926,806,9065,80,5,71,68,50,91,111,30,58,148}  
c47 | {69608,3397,69214,21894,5231,92972,36696,48935,85503,45654,49,121,141,57,100,99,54,94,104,55}  
c48 | {71140,22280,39205,18064,67376,71903,78140,41324,91387,16578,60,92,30,14,124,38,3,29,111,131}  
c49 | {64638,6116,67292,58532,44051,33617,24049,79587,95692,93341,24,100,23,83,127,124,40,94,36,27}  
c50 | {79012,63559,78516,98686,72313,60953,23440,73888,79936,96978,91,67,5,42,4,71,92,40,40,86}  
c51 | {19894,41908,23496,35213,96590,7941,17758,23024,70375,41477,61,74,8,29,72,116,120,107,76,90}  
c52 | {67889,11450,3921,70683,39257,6576,17377,530,33128,43508,86,80,128,121,132,123,133,9,7,88}  
c53 | {46869,45123,7791,51604,64032,55412,28502,43744,26323,79136,5,141,136,11,97,45,20,123,45,70}  
c54 | {25178,87116,99137,10293,67656,86921,91847,55986,92314,96275,22,59,62,34,136,8,116,29,73,6}  
c55 | {97823,51814,97527,88109,58677,61970,17501,71964,43640,47272,28,103,52,26,118,3,6,106,87,145}  
c56 | {66630,71970,35032,7726,94002,25368,12705,71295,44055,61277,112,63,20,108,45,107,51,71,65,116}  
c57 | {94158,61360,45962,28245,78426,24621,29838,82264,94976,87266,118,92,89,20,104,80,58,123,36,124}  
c58 | {42712,98691,23844,55502,70678,53379,26818,4484,265,69948,123,142,47,42,34,14,78,78,138,71}  
c59 | {39169,69661,8193,98104,82656,77075,50890,20869,58510,74821,5,2,110,40,85,66,120,125,73,120}  
c60 | {52889,29852,74145,83896,57293,96361,93179,9204,48264,84576,84,131,81,96,128,55,62,54,86,149}  
c61 | {10646,60146,190,83857,86676,56463,27596,66435,39404,75669,70,138,8,31,114,94,25,104,108,97}  
c62 | {53342,27357,51760,7277,91190,36845,43718,31948,72670,878,47,125,92,47,101,71,131,142,21,40}  
c63 | {69836,59842,18662,75056,79995,94400,37418,96359,63166,8834,92,25,54,19,36,41,74,101,89,33}  
c64 | {67779,89791,4675,28502,20745,71397,75751,8011,65565,89284,52,53,74,80,16,44,71,71,38,16}  
c65 | {56107,85714,26515,91997,98009,49849,18926,46998,16751,77652,103,127,101,110,19,132,67,133,144,15}  
c66 | {77465,30127,44793,26290,83019,54798,54960,30160,2072,79839,61,87,98,100,75,95,25,103,15,50}  
c67 | {46151,78465,17210,13293,51346,29791,1029,95475,17972,96626,8,143,40,75,32,14,7,115,59,10}  
c68 | {55925,79647,64106,21177,46179,13831,84287,62410,82330,94244,143,43,109,19,62,36,63,64,29,90}  
c69 | {38828,23945,54898,65279,73454,76174,74600,77610,52260,13930,126,12,140,72,44,59,92,20,3,66}  
c70 | {7291,96804,71685,79699,8954,13008,3303,50744,55210,22232,16,141,69,98,89,29,62,50,145,140}  

2、動態寫入對應分區的方法

PostgreSQL 10

create or replace function ff(  
  int,  -- 分區號  
  int   -- 寫入記錄數  
) returns void as $$  
declare  
begin  
  execute 'insert into test'||$1||' select random()*100 from generate_series(1,'||$2||')';  
end;  
$$ language plpgsql strict;  

3、直接寫入單表的方法

PostgreSQL 10

vi test.sql  
insert into test select random()*100 from generate_series(1,100);  
  
nohup pgbench -M prepared -n -r -P 5 -f ./test.sql -c 64 -j 64 -t 50000 >/tmp/log 2>&1 &  

約6.2萬行每秒,性能取決於單行的大小。

我這邊的測試3.2億數據占用977GB,如果單行變小,性能會隨之提升。

transaction type: ./test.sql  
scaling factor: 1  
query mode: prepared  
number of clients: 64  
number of threads: 64  
number of transactions per client: 50000  
number of transactions actually processed: 3200000/3200000  
latency average = 102.605 ms  
latency stddev = 29.016 ms  
tps = 622.235371 (including connections establishing)  
tps = 622.236656 (excluding connections establishing)  
script statistics:  
 - statement latencies in milliseconds:  
       102.611  insert into test select random()*100 from generate_series(1,100);  
  
  
postgres=# \dt+ test  
                    List of relations  
 Schema | Name | Type  |  Owner   |  Size  | Description   
--------+------+-------+----------+--------+-------------  
 public | test | table | postgres | 977 GB |   
(1 row)  

4、寫入阿裏雲HybridDB for PostgreSQL

-- Greenplum  
  
vi test.sql  
  
\timing  
insert into test select random()*100 from generate_series(1,320000000);  
  
nohup psql -f ./test.sql >/tmp/log_gpdb 2>&1 &  

GPDB 列存,壓縮後458GB。

postgres=# select pg_size_pretty(pg_total_relation_size('test'));  
 pg_size_pretty   
----------------  
 458 GB  
(1 row)  

6、也可以從PG導出到HDB,例子

date  
  psql -c "copy test to stdout"|psql -U dege.zzz -p 15432 -d postgres -c "copy test from stdin"   
date  

7、大多數時候,生產環境使用的是 從OSS導入HDB或RDS PG的方法。

手冊如下

https://help.aliyun.com/document_detail/44461.html

https://help.aliyun.com/document_detail/35457.html

透視測試(阿裏雲HybridDB for PostgreSQL)

1、符合條件的設備、會員計數

1.1 HybridDB for PostgreSQL (列存儲) 硬算

等值查詢:

postgres=# select count(*) from test where c2=1;  
 count   
-------  
 32233  
(1 row)  
Time: 581.304 ms  

數組(多值類型)相交(包含任意)查詢:

postgres=# select count(*) from test where c41 && array [1,2,1000];  
  count     
----------  
 40227896  
(1 row)  
Time: 10227.078 ms  

等值 以及 數組(多值類型)相交(包含任意)查詢:

postgres=# select count(*) from test where c40=1 and c41 && array [1,2,1000];  
 count    
--------  
 401802  
(1 row)  
Time: 6011.031 ms  

等值 或 數組(多值類型)相交(包含任意)查詢:

postgres=# select count(*) from test where c40=1 or c41 && array [1,2,1000];  
  count     
----------  
 43025528  
(1 row)  
Time: 9945.750 ms  

數組(多值類型)包含(包含所有)查詢:

postgres=# select count(*) from test where c41 @> array [1,2,1000];  
 count   
-------  
   123  
(1 row)  
Time: 10934.176 ms  

1.2 采用統計信息進行估算

《論count與offset使用不當的罪名 和 分頁的優化》

1.3 采用估值類型hll進行估算

《Greenplum 最佳實踐 - 估值插件hll的使用(以及hll分式聚合函數優化)》

2、符合條件的數據多值列、普通列透視(TOP N)

2.1 透視多值列:

postgres=# select unnest(c41) c41, count(*) from test where c2=1 group by 1 order by 2 desc limit 100;  
 c41 | count   
-----+-------  
  72 |  2276  
 132 |  2255  
  65 |  2250  
 130 |  2244  
  84 |  2241  
......  
  41 |  2137  
  31 |  2137  
 137 |  2135  
(100 rows)  
  
Time: 8058.408 ms  

2.2 透視標量列:

postgres=# select c40, count(*) from test where c2=1 group by 1 order by 2 desc limit 100;  
 c40 | count   
-----+-------  
  40 |   363  
   5 |   358  
   2 |   356  
  93 |   355  
  67 |   353  
  18 |   351  
  99 |   350  
......  
  86 |   288  
  71 |   287  
  84 |   277  
   0 |   160  
(100 rows)  
  
Time: 1328.181 ms  

圈選測試(阿裏雲HybridDB for PostgreSQL)

3、圈選符合條件的設備ID、用戶ID。

采用遊標圈選目標人群,每一頁都絲般柔滑。

postgres=# begin;  
BEGIN  
Time: 0.764 ms  
  
postgres=# declare cur1 cursor for select c1 from test where c2=1;  
DECLARE CURSOR  
Time: 215.695 ms  
  
postgres=# fetch 10 from cur1;  
 c1   
----  
 44  
 50  
 86  
 48  
 27  
 71  
 10  
 83  
 24  
 96  
(10 rows)  
  
Time: 0.288 ms  
postgres=# fetch 10 from cur1;  
 c1   
----  
 39  
 16  
 24  
 90  
 25  
  1  
 41  
 33  
 76  
  0  
(10 rows)  
  
Time: 0.087 ms  

圈選3萬人群:

postgres=# explain analyze select c1 from test where c2=1 ;  
                                       QUERY PLAN                                          
-----------------------------------------------------------------------------------------  
 Gather Motion 48:1  (slice1; segments: 48)  (cost=0.00..35186640.00 rows=31953 width=4)  
   Rows out:  32233 rows at destination with 877 ms to end, start offset by 1.299 ms.  
   ->  Append-only Columnar Scan on test  (cost=0.00..35186640.00 rows=666 width=4)  
         Filter: c2 = 1  
         Rows out:  0 rows (seg0) with 10 ms to end, start offset by 62 ms.  
 Slice statistics:  
   (slice0)    Executor memory: 347K bytes.  
   (slice1)    Executor memory: 478K bytes avg x 48 workers, 494K bytes max (seg2).  
 Statement statistics:  
   Memory used: 128000K bytes  
 Settings:  optimizer=off  
 Optimizer status: legacy query optimizer  
 Total runtime: 878.970 ms  
(13 rows)  
  
Time: 880.017 ms  

圈選5800萬人群:

postgres=# explain analyze select c1 from test where c41 && array[1,2,100];  
                                        QUERY PLAN                                           
-------------------------------------------------------------------------------------------  
 Gather Motion 48:1  (slice1; segments: 48)  (cost=0.00..35186640.00 rows=1600000 width=4)  
   Rows out:  58538502 rows at destination with 23842 ms to end, start offset by 1.282 ms.  
   ->  Append-only Columnar Scan on test  (cost=0.00..35186640.00 rows=33334 width=4)  
         Filter: c41 && '{1,2,100}'::integer[]  
         Rows out:  0 rows (seg0) with 7.488 ms to end, start offset by 35 ms.  
 Slice statistics:  
   (slice0)    Executor memory: 347K bytes.  
   (slice1)    Executor memory: 494K bytes avg x 48 workers, 494K bytes max (seg0).  
 Statement statistics:  
   Memory used: 128000K bytes  
 Settings:  optimizer=off  
 Optimizer status: legacy query optimizer  
 Total runtime: 23843.827 ms  
(13 rows)  
  
Time: 23845.061 ms  

圈選6萬人群:

postgres=# explain analyze select c1 from test where c41 @> array[1,2,100];  
                                        QUERY PLAN                                          
------------------------------------------------------------------------------------------  
 Gather Motion 48:1  (slice1; segments: 48)  (cost=0.00..35186640.00 rows=320000 width=4)  
   Rows out:  63866 rows at destination with 8920 ms to end, start offset by 1.375 ms.  
   ->  Append-only Columnar Scan on test  (cost=0.00..35186640.00 rows=6667 width=4)  
         Filter: c41 @> '{1,2,100}'::integer[]  
         Rows out:  0 rows (seg0) with 28 ms to end, start offset by 13 ms.  
 Slice statistics:  
   (slice0)    Executor memory: 347K bytes.  
   (slice1)    Executor memory: 490K bytes avg x 48 workers, 494K bytes max (seg0).  
 Statement statistics:  
   Memory used: 128000K bytes  
 Settings:  optimizer=off  
 Optimizer status: legacy query optimizer  
 Total runtime: 8921.803 ms  
(13 rows)  
  
Time: 8922.994 ms  

鍵值查詢測試(阿裏雲RDS for PostgreSQL)

4、根據設備ID、用戶ID 點查

(建立ID索引,本例使用BLOCK NUM進行測試,實際效果一樣)

postgres=# select c1 from test where ctid='(1,1)';  
 c1   
----  
 49  
(1 row)  
  
Time: 0.408 ms  

壓測準備

postgres=# show block_size;  
 block_size   
------------  
 32768  
(1 row)  
postgres=# analyze test;  
ANALYZE  
postgres=# select relpages from pg_class where relname='test';  
 relpages   
----------  
 32000002  
(1 row)  
postgres=# select c1 from test where ctid='(1,10)';  
 c1   
----  
 28  
(1 row)  
postgres=# select c1 from test where ctid='(1,11)';  
 c1   
----  
(0 rows)  

壓測

vi test.sql  
  
\set x random(1,32000002)  
\set y random(1,10)  
select * from test where ctid=('('||:x||','||:y||')')::tid;  

單次響應1.1毫秒

transaction type: ./test.sql  
scaling factor: 1  
query mode: extended  
number of clients: 64  
number of threads: 64  
duration: 120 s  
number of transactions actually processed: 6762499  
latency average = 1.136 ms  
latency stddev = 6.042 ms  
tps = 56349.372585 (including connections establishing)  
tps = 56353.497075 (excluding connections establishing)  
script statistics:  
 - statement latencies in milliseconds:  
         0.001  \set x random(1,32000002)  
         0.000  \set y random(1,10)  
         1.135  select * from test where ctid=('('||:x||','||:y||')')::tid;  

由於數據977GB,超過了內存大小,如果全網數據都是熱數據,磁盤讀取速率達到11GB/s。

  1   1  98   0   0   0|  90M   33M|   0     0 |   0     0 |9775    18k  
 36  21   8  34   0   1|  11G    0 | 239B  446B|   0     0 | 426k  369k  
 37  21   8  33   0   1|  11G 4096B| 120B  334B|   0     0 | 430k  374k  
 37  22   7  32   0   2|  11G  660k| 239B  727B|   0     0 | 433k  383k  
 26  44   5  23   0   1|8313M    0 | 120B  753B|   0     0 | 307k  260k  
 35  28   7  29   0   1|  11G  172k| 477B 1183B|   0     0 | 390k  328k  
 36  17   9  37   0   2|  11G    0 | 344B 2385B|   0     0 | 441k  381k  
 33  26   8  32   0   1|  10G    0 |1449B 1093B|   0     0 | 396k  333k  
 31  34   7  26   0   1|9585M    0 | 120B  588B|   0     0 | 347k  303k  

性能指標

環境規格

HybridDB for PostgreSQL規格:48C SSD。

RDS PostgreSQL規格:60核。

性能數據

1、符合條件的設備、會員計數

1.1 等值查詢:

0.5 秒

1.2 數組(多值類型)相交(包含任意)查詢:

10 秒

1.3 等值 以及 數組(多值類型)相交(包含任意)查詢:

6 秒

1.4 等值 或 數組(多值類型)相交(包含任意)查詢:

10 秒

1.5 數組(多值類型)包含(包含所有)查詢:

10 秒

2、符合條件的數據多值列、普通列透視(TOP N)

2.1 透視多值列:

8 秒

2.2 透視標量列:

1.3 秒

3、圈選符合條件的設備ID、用戶ID。

采用遊標,每一頁效率一致

3.1 標量條件,圈選3萬人群:

總耗時 0.9 秒

3.2 多值條件,圈選5850萬人群:

總耗時 24 秒

3.3 多值條件,圈選6萬人群:

總耗時 9 秒

4、根據設備ID、用戶ID 點查

1.1 毫秒

雲端產品

阿裏雲 RDS PostgreSQL

阿裏雲 HybridDB for PostgreSQL

阿裏雲 海量對象存儲OSS

小結

視頻網站,已經滲透到各種終端,除了喜聞樂見的手機,還包括移動終端、電腦、盒子、電視、投影儀等。有設備屬性、會員屬性、渠道屬性等。

內容運營是非常重要的環節,而透視則是運營的重要武器。

通過阿裏雲RDS PostgreSQL, HybridDB for PostgreSQL,可以高效的滿足(時間、空間(GIS)、多值列、單值列)的多維數據透視、人群圈選、點查等需求。

最後更新:2017-08-28 10:32:40

  上一篇:go  PostgreSQL 助力企業打開時空之門 - 阿裏雲(RDS、HybridDB) for PostgreSQL最佳實踐
  下一篇:go  海量實時計算+OLTP+OLAP DB設計 - 阿裏雲(RDS、HybridDB) for PostgreSQL最佳實踐 - 泛電網係統應用