音视图(泛内容)网站透视分析 DB设计 - 阿里云(RDS、HybridDB) for PostgreSQL最佳实践

本文涉及的产品
云原生数据库 PolarDB MySQL 版,Serverless 5000PCU 100GB
云原生数据库 PolarDB 分布式版,标准版 2核8GB
云数据库 RDS MySQL Serverless,0.5-2RCU 50GB
简介:

标签

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)、多值列、单值列)的多维数据透视、人群圈选、点查等需求。

相关实践学习
基于CentOS快速搭建LAMP环境
本教程介绍如何搭建LAMP环境,其中LAMP分别代表Linux、Apache、MySQL和PHP。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助     相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
相关文章
|
19天前
|
关系型数据库 分布式数据库 数据库
成都晨云信息技术完成阿里云PolarDB数据库产品生态集成认证
近日,成都晨云信息技术有限责任公司(以下简称晨云信息)与阿里云PolarDB PostgreSQL版数据库产品展开产品集成认证。测试结果表明,晨云信息旗下晨云-站群管理系统(V1.0)与阿里云以下产品:开源云原生数据库PolarDB PostgreSQL版(V11),完全满足产品兼容认证要求,兼容性良好,系统运行稳定。
|
2月前
|
关系型数据库 分布式数据库 数据库
阿里云PolarDB登顶2024中国数据库流行榜:技术实力与开发者影响力
近日,阿里云旗下的自研云原生数据库PolarDB在2024年中国数据库流行度排行榜中夺冠,并刷新了榜单总分纪录,这一成就引起了技术圈的广泛关注。这一成就源于PolarDB在数据库技术上的突破与创新,以及对开发者和用户的实际需求的深入了解体会。那么本文就来分享一下关于数据库流行度排行榜的影响力以及对数据库选型的影响,讨论PolarDB登顶的关键因素,以及PolarDB“三层分离”新版本对开发者使用数据库的影响。
74 3
阿里云PolarDB登顶2024中国数据库流行榜:技术实力与开发者影响力
|
2月前
|
关系型数据库 分布式数据库 数据库
阿里云瑶池数据库训练营权益:PolarDB开发者大会主题资料开放下载!
阿里云瑶池数据库训练营权益:PolarDB开发者大会主题资料开放下载!
|
3月前
|
域名解析 弹性计算 数据安全/隐私保护
阿里云ECS免费搭建WordPress个人博客网站
阿里云ECS免费搭建WordPress个人博客网站
444 2
阿里云ECS免费搭建WordPress个人博客网站
|
28天前
|
域名解析 弹性计算 数据可视化
如何使用云服务器ECS搭建网站?阿里云云服务器ECS部署网站新手教程
本文介绍了在阿里云ECS上自助建站的流程。建站方式包括自助建站、模板建站和定制建站,适合个人或小企业用户。建站步骤涉及准备服务器、配置安全组规则、部署网站、购买和备案域名以及解析域名。此外,文章还提及了产品计费和常见问题,提供相关服务如架构设计、迁云服务和基础设置服务等。
376 0
|
30天前
|
存储 关系型数据库 MySQL
TiDB与MySQL、PostgreSQL等数据库的比较分析
【2月更文挑战第25天】本文将对TiDB、MySQL和PostgreSQL等数据库进行详细的比较分析,探讨它们各自的优势和劣势。TiDB作为一款分布式关系型数据库,在扩展性、并发性能等方面表现突出;MySQL以其易用性和成熟性受到广泛应用;PostgreSQL则在数据完整性、扩展性等方面具有优势。通过对比这些数据库的特点和适用场景,帮助企业更好地选择适合自己业务需求的数据库系统。
|
1月前
|
Cloud Native 关系型数据库 分布式数据库
热烈祝贺阿里云PolarDB登顶2024最新一期中国数据库流行榜
【2月更文挑战第3天】热烈祝贺阿里云PolarDB登顶2024最新一期中国数据库流行榜
|
2月前
|
Cloud Native 关系型数据库 分布式数据库
2024最新一期中国数据库流行榜公布:阿里云PolarDB登顶
PolarDB登顶国产数据库流行榜,持续引领云原生数据库创新
2024最新一期中国数据库流行榜公布:阿里云PolarDB登顶
|
3月前
|
存储 关系型数据库 分布式数据库
阿里云PolarDB解决乐麦多源数据存储性能问题
乐麦通过使用PolarDB数据库,使整个系统之间的数据查询分析更加高效
390 3
|
3月前
|
关系型数据库 Serverless 分布式数据库
碧桂园服务使用阿里云PolarDB Serverless云数据库实现降本增效。
碧桂园集团,即碧桂园控股有限公司新型城镇化住宅开发商,采用集中及标准化的运营模式,业务包含物业发展、建安、装修、物业管理、物业投资、酒店开发和管理、以及现代农业、机器人。

相关产品

  • 云数据库 RDS MySQL 版
  • 云原生数据库 PolarDB