云端海量任务调度系统数据库设计 - 阿里云RDS PostgreSQL案例

标签

PostgreSQL , 任务调度系统 , 数据库设计 , schemaless


背景

任务调度系统中的任务状态管理,通常会用到数据库来存储任务调度的过程状态,控制任务的锁等。

《advisory lock 实现高并发非堵塞式 业务锁》

如果是小量任务,是挺好实现的,但是每小时处理几十亿或者几亿的任务,如何设计这样的任务状态管理数据库呢?

挑战

对于一个面向多个用户的任务调度平台(例如云端的任务调度平台,将面向所有租户使用)。

较大的挑战是任务数据的写入(海量),另一个是任务状态的更新(海量,每个任务至少被更新一次)。

云端海量任务调度数据库设计

云端任务调度存在一些特性:

1、用户和用户之间的任务是没有关系的,单个用户的任务在调度时可能有依赖关系。

2、数据量庞大。

3、任务通常都有最终稳定状态,稳定后,对应的任务记录就不会变化了。

针对以上几个特点,采样PostgreSQL设计:

1、任务数据生成后写入任务处理表

2、任务处理表使用rotate设计(例如每小时一个rotate表),处理完的数据直接清除,不需要VACUUM。

3、分区方面,任务处理表采样用户级分区,在获取需要处理的任务时更加的精炼(减少冗余扫描)。

4、当任务达到最终状态时,从任务运行表删除,写入历史表。

5、早期的历史表,从RDS PG中删除,写入阿里云OSS,使用RDS PG OSS外部表接口可以访问到这些历史数据。

DEMO设计

1、初始任务表,用于存储用户生成的任务。

create table task_init (    -- 任务初始表
  uid  int,        -- 用户id
  ptid serial8,    -- 父任务id
  tid serial,      -- 子任务ID
  state int default 1,    -- 任务状态,1表示初始状态,-1表示正在处理, 0表示处理结束
  retry int default -1,   -- 重试次数
  info text,              -- 其他信息
  ts timestamp            -- 时间
);

2、任务历史表,用于存储任务的最终状态。

create table task_hist (    -- 任务历史表
  uid  int,   -- 用户id
  ptid int8,  -- 父任务id
  tid int,    -- 子任务ID
  state int default 1,    -- 任务状态,1表示初始状态,-1表示正在处理, 0表示处理结束
  retry int default -1,   -- 重试次数
  info text,              -- 其他信息
  ts timestamp      -- 时间
);

3、为了简化测试,按用户ID进行分区。(前面提到的rotate设计,多级分区设计,请参考本文末尾的文章)

do language plpgsql $$
declare
begin
  for i in 1..1000 loop
    execute 'create table task_init_'||i||' ( like task_init including all)';
    execute 'create table task_hist_'||i||' ( like task_hist including all)';
  end loop;
end;
$$;

4、为了测试方便,使用schemaless的设计,将用户任务的初始数据生成写入放在PLPGSQL逻辑中。

create or replace function ins_task_init(
  uid int,
  info text,
  ts timestamp
)  returns void as $$
declare
  target name;
begin
  target := format('%I', 'task_init_'||uid);
  execute format('insert into %I (uid,info,ts) values (%L,%L,%L)', target, uid,info,ts);
end;
$$ language plpgsql strict;

5、运行任务,分为几个步骤。

5.1、从任务表读取任务。

5.2、用户执行任务。

5.3、反馈执行的结果,不成功的任务更新task_init表,对于执行成功(并结束)的任务,数据从task_init迁移到task_hist。

为了测试数据库的性能,我讲这三步的逻辑写到plpgsql里面。同时使用delete limit的特性,一次批量取出若干条任务。

这里使用CTID行号定位,达到最佳的性能。不仅免去了索引的使用,而且性能更佳。

这里使用了advisory lock,使得单个用户不会出现并行任务。(实际业务中,可以并行。)

这里没有测试更新状态,task_init还有少量更新(相比insert和delete,比例很少,可以忽略),比如任务失败的情况。

关闭task_init表的autovacuum,采用rotate的形式进行处理。

create or replace function run_task(
  uid int,
  batch int
) returns void as $$
declare
  target1 name;
  target2 name;
begin
  target1 := format('%I', 'task_init_'||uid);
  target2 := format('%I', 'task_hist_'||uid);
  execute format('with t1 as (select ctid from %I where pg_try_advisory_xact_lock(%L) limit %s) , t2 as (delete from %I where ctid = any (array(select ctid from t1)) returning *)  insert into %I select * from t2;', target1, uid, batch, target1, target2);
end;
$$ language plpgsql strict;

6、测试分解动作。

写入初始任务  

postgres=# select ins_task_init(1,'test',now()::timestamp);
 ins_task_init
---------------  

(1 row)  

postgres=# select ins_task_init(1,'test',now()::timestamp);
 ins_task_init
---------------  

(1 row)  

运行任务  

postgres=# select run_task(1,100);
 run_task
----------  

(1 row)  

查看任务是否结束并迁移到历史表  

postgres=# select * from task_init_1;
 uid | ptid | tid | state | retry | info | ts
-----+------+-----+-------+-------+------+----
(0 rows)  

postgres=# select * from task_hist_1;
 uid | ptid | tid | state | retry | info |             ts
-----+------+-----+-------+-------+------+----------------------------
   1 |    1 |   1 |     1 |    -1 | test | 2017-07-20 15:26:32.739766
   1 |    2 |   2 |     1 |    -1 | test | 2017-07-20 15:26:33.233469
(2 rows)

性能压测

1、生成任务的性能

vi ins.sql
\set uid random(1,1000)
select ins_task_init(:uid,'test',now()::timestamp);   

pgbench -M prepared -n -r -P 1 -f ./ins.sql -c 32 -j 32 -T 120
query mode: prepared
number of clients: 64
number of threads: 64
duration: 360 s
number of transactions actually processed: 86074880
latency average = 0.268 ms
latency stddev = 0.295 ms
tps = 239079.558174 (including connections establishing)
tps = 239088.708200 (excluding connections establishing)
script statistics:
 - statement latencies in milliseconds:
         0.001  \set uid random(1,1000)
         0.267  select ins_task_init(:uid,'test',now()::timestamp);  

postgres=# select count(*) from task_init_1;
 count
-------
 88861
(1 row)  

postgres=# select count(*) from task_init_2;
 count
-------
 88196
(1 row)  

....  

postgres=# select count(*) from task_init_1000;
 count
-------
 88468
(1 row)

2、运行任务的性能(一次批量取10000条任务)

vi run.sql
\set uid random(1,1000)
select run_task(:uid,10000);  

pgbench -M prepared -n -r -P 1 -f ./run.sql -c 32 -j 32 -T 120  

query mode: prepared
number of clients: 32
number of threads: 32
duration: 120 s
number of transactions actually processed: 3294
latency average = 1171.228 ms
latency stddev = 361.056 ms
tps = 27.245606 (including connections establishing)
tps = 27.247560 (excluding connections establishing)
script statistics:
 - statement latencies in milliseconds:
         0.003  \set uid random(1,1000)
      1171.225  select run_task(:uid,10000);  

postgres=# select count(*) from task_init_1000;
 count
-------
 18468
(1 row)  

postgres=# select count(*) from task_hist_1000;
 count
--------
 224207
(1 row)

单独的测试数据

1、生成任务,23.9万条/s

2、消耗任务,27.2万条/s

生成与消耗任务同时运行的测试数据

1、生成任务,16.8万条/s

2、消耗任务,大于16.8万条/s

没有任何任务堆积。

小结

PostgreSQL在云端海量任务调度系统中,发挥了重要的作用。

单个PostgreSQL实例,已经可以处理每个小时 的任务生成,以及 的任务消耗。

任务调度系统比MQ更加复杂,类似MQ的超集,所以用户如果有MQ的需求,实际上使用RDS PostgreSQL也是可以的。性能指标比上面的测试更好。

参考

《advisory lock 实现高并发非堵塞式 业务锁》

《PostgreSQL schemaless 的实现(类mongodb collection)》

《行为、审计日志 (实时索引/实时搜索)建模 - 最佳实践 2》

《在PostgreSQL中实现update | delete limit》

《块级(ctid)扫描在IoT(物联网)极限写和消费读并存场景的应用》

《PostgreSQL 10.0 preview 功能增强 - 内置分区表》

《PostgreSQL 9.5+ 高效分区表实现 - pg_pathman》

《PostgreSQL 数据rotate用法介绍 - 按时间覆盖历史数据》

时间: 2024-11-02 03:23:32

云端海量任务调度系统数据库设计 - 阿里云RDS PostgreSQL案例的相关文章

贷款、天使投资(风控助手)业务数据库设计 - 阿里云RDS PostgreSQL, HybridDB for PostgreSQL最佳实践

标签 PostgreSQL , HybridDB for PostgreSQL , 小微贷款 , 金融风控 , 企业图谱 , 图式搜索 , 舆情分析 , 自动贷款 , 贷款审查 , 审查神器 背景 贷款是银行的主营业务之一,但是并不是只有银行能提供贷款,实际上资金雄厚的公司都有能力提供贷款(比如保险行业.资源垄断型企业等). 除了放贷,我们常说的天使投资.A轮B轮啥的,也是类似的场景,凭什么投你,背后如何决策也需要决策系统的支撑. 与贷款相反的是吸金类业务,比如我们现在发现越来越多的理财产品.股

(时序业务)证券交易系统数据库设计 - 阿里云RDS PostgreSQL最佳实践

标签 PostgreSQL , 证券 , 时序数据 , JSON , HSTORE , 数组 , range索引 , BRIN块级索引 , 分时走势 , 线性回归 , MADlib , 机器学习 背景 证券行业产生的数据比较多,读写非常频繁. 以股票交易为例,一共有几千只股票.一年大概有240个交易日,交易日大概是从早上10点到下午4点. 1.数据写入需求: 实时的数据写入,按查询维度的实时数据合并(比如秒数据实时写入.分钟,几分钟,...则实时合并). 数据分为不同粒度的分时数据.(精确到秒,

时间、空间、对象 海量极速多维检索 - 阿里云RDS PostgreSQL最佳实践

标签 PostgreSQL , 时间 , 空间 , 对象属性 , 多维度检索 , 海量 , 空间索引 , 数据分区 , 块级索引BRIN , 多级索引 , GIN倒排索引 , JSON索引 , 多列索引 , 多索引扫描合并 , bitmapAnd , bitmapOr , 物理扫描 , ctid扫描 , intersect , partial index , partition index 背景 人类或者其他对象的活动产生了海量的时间.空间数据,如果有科技能实现回到过去,过去的世界状态会是什么样

机票业务(单实例 2700万行/s return)数据库架构设计 - 阿里云RDS PostgreSQL最佳实践

背景 机票业务的某个模块,数据量10亿+,写.更新.删除量较低.根据KEY查询一些数据,每次查询返回1万条左右的记录. 就是这样简单的需求,业务方发现读成为了巨大的瓶颈,每次返回1万条,100个并发请求,每秒就是100万条(500MB左右),主要的瓶颈: 1.网络是个较大的开销. 2.不同KEY的数据可能是分散存放的,存在查询时的IO放大,可能有一定的性能影响. 3.每次请求的返回记录数较多,数据库search buffer调用可能开销会上升. 就这几个问题,我们来看看如何优化或解决业务方的问题

医疗大健康行业案例(老人健康实时监测和预警) - 阿里云RDS PostgreSQL最佳实践

标签 PostgreSQL , pipelineDB , 流式计算 , 独立事件相关性 , 舆情分析 , 实时状态分析 , 递归查询 , 时序数据 背景 人的身体和机器差不多,随着年龄的增长,器官逐渐老化,毛病也会越来越多,注意保养是一方面,另一方面也需要注意实时的监测和发出预警,在问题萌芽状态就解决掉. 以往我们检查身体得去医院或专业的体检机构,很麻烦,随着科技的进步,一些健康指标的监测变得更加方便,例如手环也是一个普及很快的监控检测终端(目前已能够检测心跳.温度.运动等各项指标),未来这种终

数据寻龙点穴(空间聚集分析) - 阿里云RDS PostgreSQL最佳实践

标签 PostgreSQL , Greenplum , PostGIS , K-Mean , 热力图 背景 最近鬼吹灯热播,胡八一的<十六字阴阳风水秘术>到底是什么武功秘籍?寻龙点穴又是什么?别问我,不知道. PS:截取自互联网.- 寻龙点穴是风水学术语.古人说:三年寻龙,十年点穴.意思就是说,学会寻龙脉要很长的时间,但要懂得点穴,并且点得准则难上加难,甚至须要用"十年"时间. 但是,若没正确方法,就是用百年时间,也不能够点中风水穴心聚气的真点,这样一来,寻龙的功夫也白费了

空间索引(GiST、BRIN、R-Tree)选择、优化 - 阿里云RDS PostgreSQL最佳实践

标签 PostgreSQL , Greenplum , PostGIS , GiST , R-Tree , BRIN , 相关性 , 网格 , BOX , K-Mean 背景 空间数据的搜索需求通常包括: 1.平面.三维.多维对象 几何相交.不相交.相邻. 2.平面.三维.多维对象的方位判断(相交或严格在左边.右边.上边.下边),类似数值的大于.小于.大于等于.小于等于. 3.平面.三维.多维对象 包含 另一个对象 4.平面.三维.多维对象 等于 另一个对象 5.平面.三维.多维对象 与另一个对

海量实时计算+OLTP+OLAP DB设计 - 阿里云(RDS、HybridDB) for PostgreSQL最佳实践 - 泛电网系统应用

标签 PostgreSQL , 国家电网 , 电表 , 余额 , 流式计算 , 状态监测 , 上下文相关 背景 电网系统是一个关系民生,又非常典型的传统系统,虽然传统,量可不小.在互联网化(物联网化)的今天,有很多值得借鉴和思考的点供给其他相关系统参考. 每个省份大概有亿级户电表,最大的地市可能有千万户级别. 以往我们电费是怎么交的呢?我们小区是两个月交一次,也就是说先消费,再付款的方式.这么说起来电网真的是很仁义啊,现在哪有这么多先消费再付款的呀.移动话费.家庭宽带.天然气等等,都是充值后使用

云端流计算、在线业务、实时分析 闭环设计 - 阿里云RDS、HybridDB for PostgreSQL最佳实践

背景 水的流动汇成江河大海,孕育生命,形成大自然生态.数据流动,推进社会进步,拓展业务边界. <从人类河流文明 洞察 数据流动的重要性> 以某淘系业务案例展开,看看用户如何利用阿里云RDS PostgreSQL,HybridDB for PostgreSQL,海量对象存储OSS,打造一个从流计算到在线业务,再到数据分析和挖掘的业务,发挥数据的价值,拓展业务的边界. 业务简介 一个电商业务通常会涉及 商家.门店.物流.用户.支付渠道.贷款渠道.商品.平台.小二.广告商.厂家.分销商.店主.店员.