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

本文涉及的产品
RDS SQL Server Serverless,2-4RCU 50GB 3个月
推荐场景:
云原生数据库 PolarDB 分布式版,标准版 2核8GB
RDS PostgreSQL Serverless,0.5-4RCU 50GB 3个月
推荐场景:
对影评进行热评分析
简介:

标签

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


背景

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

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

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

挑战

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

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

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

pic

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

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            -- 时间     
);     
AI 代码解读

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      -- 时间      
);     
AI 代码解读

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;  
$$;  
AI 代码解读

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;  
AI 代码解读

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;  
AI 代码解读

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)  
AI 代码解读

性能压测

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)  
AI 代码解读

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)  
AI 代码解读

单独的测试数据

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用法介绍 - 按时间覆盖历史数据》

相关实践学习
如何快速连接云数据库RDS MySQL
本场景介绍如何通过阿里云数据管理服务DMS快速连接云数据库RDS MySQL,然后进行数据表的CRUD操作。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助     相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
目录
打赏
0
0
0
0
20702
分享
相关文章
数据库运维:mysql 数据库迁移方法-mysqldump
本文介绍了MySQL数据库迁移的方法与技巧,重点探讨了数据量大小对迁移方式的影响。对于10GB以下的小型数据库,推荐使用mysqldump进行逻辑导出和source导入;10GB以上可考虑mydumper与myloader工具;100GB以上则建议物理迁移。文中还提供了统计数据库及表空间大小的SQL语句,并讲解了如何使用mysqldump导出存储过程、函数和数据结构。通过结合实际应用场景选择合适的工具与方法,可实现高效的数据迁移。
126 1
大数据大厂之MySQL数据库课程设计:揭秘MySQL集群架构负载均衡核心算法:从理论到Java代码实战,让你的数据库性能飙升!
本文聚焦 MySQL 集群架构中的负载均衡算法,阐述其重要性。详细介绍轮询、加权轮询、最少连接、加权最少连接、随机、源地址哈希等常用算法,分析各自优缺点及适用场景。并提供 Java 语言代码实现示例,助力直观理解。文章结构清晰,语言通俗易懂,对理解和应用负载均衡算法具有实用价值和参考价值。
大数据大厂之MySQL数据库课程设计:揭秘MySQL集群架构负载均衡核心算法:从理论到Java代码实战,让你的数据库性能飙升!
【YashanDB知识库】原生mysql驱动配置连接崖山数据库
【YashanDB知识库】原生mysql驱动配置连接崖山数据库
【YashanDB知识库】原生mysql驱动配置连接崖山数据库
大数据新视界 --面向数据分析师的大数据大厂之 MySQL 基础秘籍:轻松创建数据库与表,踏入大数据殿堂
本文详细介绍了在 MySQL 中创建数据库和表的方法。包括安装 MySQL、用命令行和图形化工具创建数据库、选择数据库、创建表(含数据类型介绍与选择建议、案例分析、最佳实践与注意事项)以及查看数据库和表的内容。文章专业、严谨且具可操作性,对数据管理有实际帮助。
大数据新视界 --面向数据分析师的大数据大厂之 MySQL 基础秘籍:轻松创建数据库与表,踏入大数据殿堂
MySQL下载安装全攻略!小白也能轻松上手,从此数据库不再难搞!
这是一份详细的MySQL安装与配置教程,适合初学者快速上手。内容涵盖从下载到安装的每一步操作,包括选择版本、设置路径、配置端口及密码等。同时提供基础操作指南,如数据库管理、数据表增删改查、用户权限设置等。还介绍了备份恢复、图形化工具使用和性能优化技巧,帮助用户全面掌握MySQL的使用方法。附带常见问题解决方法,保姆级教学让你无忧入门!
MySQL下载安装全攻略!小白也能轻松上手,从此数据库不再难搞!
MySQL与Clickhouse数据库:探讨日期和时间的加法运算。
这一次的冒险就到这儿,期待你的再次加入,我们一起在数据库的世界中找寻下一个宝藏。
78 9
【赵渝强老师】OceanBase数据库从零开始:MySQL模式
《OceanBase数据库从零开始:MySQL模式》是一门包含11章的课程,涵盖OceanBase分布式数据库的核心内容。从体系架构、安装部署到租户管理、用户安全,再到数据库对象操作、事务与锁机制,以及应用程序开发、备份恢复、数据迁移等方面进行详细讲解。此外,还涉及连接路由管理和监控诊断等高级主题,帮助学员全面掌握OceanBase数据库的使用与管理。
146 5
大数据新视界--大数据大厂之MySQL数据库课程设计:MySQL集群架构负载均衡故障排除与解决方案
本文深入探讨 MySQL 集群架构负载均衡的常见故障及排除方法。涵盖请求分配不均、节点无法响应、负载均衡器故障等现象,介绍多种负载均衡算法及故障排除步骤,包括检查负载均衡器状态、调整算法、诊断修复节点故障等。还阐述了预防措施与确保系统稳定性的方法,如定期监控维护、备份恢复策略、团队协作与知识管理等。为确保 MySQL 数据库系统高可用性提供全面指导。
大数据新视界--大数据大厂之MySQL数据库课程设计:MySQL 数据库 SQL 语句调优方法详解(2-1)
本文深入介绍 MySQL 数据库 SQL 语句调优方法。涵盖分析查询执行计划,如使用 EXPLAIN 命令及理解关键指标;优化查询语句结构,包括避免子查询、减少函数使用、合理用索引列及避免 “OR”。还介绍了索引类型知识,如 B 树索引、哈希索引等。结合与 MySQL 数据库课程设计相关文章,强调 SQL 语句调优重要性。为提升数据库性能提供实用方法,适合数据库管理员和开发人员。
【赵渝强老师】创建PostgreSQL的数据库
本文介绍了在PostgreSQL中通过SQL命令“create database”创建数据库的方法。首先查询系统目录pg_database以查看现有数据库集合,然后使用“create database”命令创建新数据库,并了解其在$PDATA/base目录下对应的文件夹生成。最后重新查询数据库集合确认创建结果,附带视频讲解便于理解操作步骤及注意事项。

相关产品

  • 云数据库 RDS
  • 云数据库 RDS PostgreSQL 版
  • 云数据库 RDS MySQL 版
  • 推荐镜像

    更多
    AI助理

    你好,我是AI助理

    可以解答问题、推荐解决方案等

    登录插画

    登录以查看您的控制台资源

    管理云资源
    状态一览
    快捷访问