背景
20亿用户,每个用户1000个标签,基于任意标签组合圈选、透视(业务上的需求是一次最多计算100个标签的组合)。
相当于要处理2000亿记录。
1、实时求标签组合的记录数。(即满足标签组合的用户有多少)
2、用户ID。(级满足标签组合的用户ID。)
要求实时响应。
通常你肯定会想,这个至少需要上百台机器来支撑。
但是我要给你一个惊喜,这个数据量,一台RDS PG实例即可。怎么做呢?听我道来,用最少的资源解决业务问题,用到RDS PG黑科技。
RDS PG 解决方案
方案如下:
《阿里云RDS PostgreSQL varbitx实践 - 流式标签 (阅后即焚流式批量计算) - 万亿级,任意标签圈人,毫秒响应》
优化方案,提高响应速度
1、bitmap切段
2、计算满足条件的USER COUNT值时,并行计算(使用dblink异步调用)
3、求用户ID时,使用游标,流式返回。
DEMO
1、需要用到的插件
create extension dblink;
create extension varbitx;
2、创建标签表,切段,例如20亿个用户,切成400段,每一段5000万个用户BIT。
postgres=# create table t_bitmap (
tagid int, -- 标签ID
ofid int, -- 偏移值, 乘以5000万
v varbit -- userid 比特
);
CREATE TABLE
3、创建索引(约束)
create unique index idx_t_bitmap_1 on t_bitmap (tagid, ofid);
4、创建1000个标签的BITMAP数据,每一个标签400条,每条的BIT长度为5000万位。
postgres=# do language plpgsql
$$
declare v varbit := repeat('1',5000000)::varbit;
begin
for i in 1..100 loop
for x in 0..399 loop
insert into t_bitmap values (i, x, v);
end loop;
end loop;
end;
$$
;
DO
Time: 150468.359 ms (02:30.468)
5、创建生成dblink连接的函数,重复创建不报错。我有几张阿里云幸运券分享给你,用券购买或者升级阿里云相应产品会有特惠惊喜哦!把想要买的产品的幸运券都领走吧!快下手,马上就要抢光了。
create or replace function conn(
name, -- dblink名字
text -- 连接串,URL
) returns void as
$$
declare
begin
perform dblink_connect($1, $2);
return;
exception when others then
return;
end;
$$
language plpgsql strict;
6、AND标签组合的并行计算函数(dblink 异步并行),返回USERID透视数。
create or replace function get_bitcount_and(
and_tagids int[], -- 输入标签ID数组
v_bit int, -- 求1或0的比特个数
conn text, -- 连接串
OUT cnt int8 -- 返回值, 多少个1或0
) returns setof int8 as
$$
declare
begin
for i in 0..399 loop -- 生成400个链接,因为每行5000万,20亿个BIT,刚好400条。并LOOP
perform conn('link'||i, conn); -- 连接
perform dblink_get_result('link'||i); -- 消耗掉上一次异步连接的结果,否则会报错。
-- 发送异步DBLINK调用
-- 每次操作一个bit分段,返回BIT为0或1的位数
perform dblink_send_query('link'||i, format('select bit_count(bit_and(v), %s) from t_bitmap where tagid = any (%L) and ofid=%s', v_bit, and_tagids, i));
end loop;
for i in 0..399 loop
-- 返回异步调用结果,包括所有分段
return query SELECT * FROM dblink_get_result('link'||i) as t(cnt int8);
end loop;
end;
$$
language plpgsql strict;
7、OR标签组合的并行计算函数(dblink 异步并行),返回USERID透视数。