pg_cron配置与使用

实验环境:postgresql-15.1    CentOS 7.9 x86-64

pg_cron版本:1.5.1

一 概述 

该插件源码在github上项目地址:https://github.com/citusdata/pg_cron

pg_cron 是一个简单的基于cron的 PostgreSQL(10 或更高版本)作业调度程序,它作为扩展在数据库内部运行。它使用与常规cron相同的语法,但它允许您直接从数据库调度PostgreSQL命令。您还可以使用"[1-59] seconds"来根据时间间隔安排作业。(来自官方README)

使用和OS一样的cron语法来定义执行计划:

 

缺点(已发现的):

  1. 只能指定到PG中的一个数据库中使用
  2. 对不同的job,同一时间点是可以并发执行的,但是对于同一个job,假如该job第一次还未执行完,第二次执行就到时间了,那么第二次执行就会等待第一次执行完成后才能执行(后续有实验)

二 安装

https://github.com/citusdata/pg_cron

2.1 编译安装二进制文件

下载软件包并解压

su - postgres

确认pg_config包含在$PATH中

[postgres@centos7 pkg_pg]$ which pg_config
/pg/pghome/bin/pg_config

解压并make安装

tar -xvf pg_cron-1.5.1.tar.gz
cd pg_cron-1.5.1/
make
make install 

安装完成,需要配置参数后才可以安装扩展.

2.2 配置pg_cron参数

为了在PG启动时同步启动pg_cron后台进程,你需要将pg_cron添加到shared_preload_libraries参数中.

请注意:pg_cron不会在备库中运行任何job,但是当备库提升为主库时,将会自动运行job

alter system set shared_preload_libraries='pg_cron';

该参数需要重启数据库生效,生效后,数据库才能识别其余cron.*参数的状态

pg_ctl restart

查看与pg_cron有关的参数:

postgres=# select * from pg_settings where name like 'cron%';

name                       |setting  |unit|category          |short_desc                                        |extra_desc                                                  |context   |vartype|source |min_val|max_val|enumvals                                                                      |boot_val |reset_val|sourcefile|sourceline|pending_restart|
---------------------------+---------+----+------------------+--------------------------------------------------+------------------------------------------------------------+----------+-------+-------+-------+-------+------------------------------------------------------------------------------+---------+---------+----------+----------+---------------+
cron.database_name         |postgres |    |Customized Options|Database in which pg_cron metadata is kept.       |                                                            |postmaster|string |default|       |       |NULL                                                                          |postgres |postgres |          |          |false          |
cron.enable_superuser_jobs |on       |    |Customized Options|Allow jobs to be scheduled as superuser           |                                                            |postmaster|bool   |default|       |       |NULL                                                                          |on       |on       |          |          |false          |
cron.host                  |localhost|    |Customized Options|Hostname to connect to postgres.                  |This setting has no effect when background workers are used.|postmaster|string |default|       |       |NULL                                                                          |localhost|localhost|          |          |false          |
cron.log_min_messages      |warning  |    |Customized Options|log_min_messages for the launcher bgworker.       |                                                            |sighup    |enum   |default|       |       |{debug5,debug4,debug3,debug2,debug1,info,notice,warning,error,log,fatal,panic}|warning  |warning  |          |          |false          |
cron.log_run               |on       |    |Customized Options|Log all jobs runs into the job_run_details table  |                                                            |postmaster|bool   |default|       |       |NULL                                                                          |on       |on       |          |          |false          |
cron.log_statement         |on       |    |Customized Options|Log all cron statements prior to execution.       |                                                            |postmaster|bool   |default|       |       |NULL                                                                          |on       |on       |          |          |false          |
cron.max_running_jobs      |32       |    |Customized Options|Maximum number of jobs that can run concurrently. |                                                            |postmaster|integer|default|0      |800    |NULL                                                                          |32       |32       |          |          |false          |
cron.timezone              |GMT      |    |Customized Options|Specify timezone used for cron schedule.          |                                                            |postmaster|string |default|       |       |NULL                                                                          |GMT      |GMT      |          |          |false          |
cron.use_background_workers|off      |    |Customized Options|Use background workers instead of client sessions.|                                                            |postmaster|bool   |default|       |       |NULL                                                                          |off      |off      |          |          |false          |

(9 rows)
  1. cron.database_name: 默认情况下,pg_cron后台进程的系统表创建在"postgres"数据库下,但是可以通过cron.database_name参数来指定系统表的存储位置.
  2. cron.timezone: 默认情况下pg_cron使用GMT时区的时间,但是新版本可以通过设置该参数来指定时区,中国东八区使用PRC
  3. cron.enable_superuser_jobs: 默认on,superuser可以修改/调度所有的job
  4. cron.log_min_messages: 记录log的最小粒度,默认记录等级warning
  5. cron.log_run:记录所有的job执行情况,记录到job_run_details,默认打开
  6. cron.log_statement:在执行之前记录所有cron语句,默认打开
  7. cron.max_running_jobs:可以同时运行的最大作业数,默认32
  8. cron.use_background_workers:使用后台进程执行,而不是client会话
  9. cron.host: 当使用client会话模式时,指定显示的主机名,但是当cron.use_background_workers为on时(使用后台进程模式时),该参数无效
alter system set cron.timezone='PRC';
alter system set cron.database_name='db_test';     --注意:只能一个库用,所以这里改成将要使用的库名

其他参数我们默认无需修改,根据需要自己定制参数

需要重启数据库让参数生效

pg_ctl restart

2.3 PG中创建扩展

使用superuser用户增加扩展

CREATE EXTENSION pg_cron;

假如想让普通用户也能创建cron,那么授权usage给普通用户

GRANT USAGE ON SCHEMA cron TO scott;

2.4 让pg_cron能够运行job

pg_cron有两种方式:client模式和后台进程模式

2.4.1 client模式运行pg_cron

重要:默认情况下,pg_cron使用libpq去创建新的会话连接到local database,所以需要:

1. 在pg_hba.conf中允许本地用户登录

2. 在pg_hba.conf中设置本地登录trust,或者设置.pgpass文件,实现免密登录

(有些环境禁用本地免密登录,我在此处配置.pgpass,有条件可以直接配置pg_hba.conf中trust,减少出错点)

还有特殊方案:使用本地unix domain socket 作为主机名,并在pg_hba.conf中配置相关的trust条目,这通常也比较安全(配置cron.host='/tmp')

2.4.2 后台进程模式运行pg_cron(需要调整max_worker_processes)

不太建议使用该方式,因为牵扯到max_worker_processes参数调整,和服务器资源等相关

# 使用后台进程模式,而不是使用客户端模式
cron.use_background_workers = on
# 提高max_worker_processes参数=20,该参数默认为8
max_worker_processes = 20

三 定时任务的创建和管理

环境:创建数据库db_test和用户scott


postgres=# create database db_test;
CREATE DATABASE
postgres=# create user scott password 'tiger';
CREATE ROLE
postgres=# \c db_test 
You are now connected to database "db_test" as user "postgres".
db_test=# grant all  ON schema public TO scott ;
GRANT

普通用户scott创建测试表

db_test=> create table t1 (id serial,curr_time varchar,curr_user varchar);
CREATE TABLE
添加~/.pgpass
localhost:5432:db_test:scott:tiger

3.1 创建cron类型的任务

 

3.1.1 使用cron语法

创建一条执行语句的计划

SELECT cron.schedule('56 15 * * *', $$insert into t1(curr_time,curr_user) values(now()::varchar,user)$$);

为了测试,可以定一个马上要到的时间

3.1.2 使用时间间隔

SELECT cron.schedule('10 seconds', $$insert into t1(curr_time,curr_user) values(now()::varchar,user)$$);

3.1.3 指定任务名

SELECT cron.schedule('Ever 10s insert data','10 seconds', $$insert into t1(curr_time,curr_user) values(now()::varchar,user)$$);

3.1.4 每晚上3点执行VACUUM

SELECT cron.schedule('nightly-vacuum', '0 3 * * *', 'VACUUM');

3.2 查询job清单

select * from cron.job;

db_test=> select * from cron.job;
 jobid |  schedule   |                             command                             | nodename  | nodeport | database | username | active |       jobname        
-------+-------------+-----------------------------------------------------------------+-----------+----------+----------+----------+--------+----------------------
     1 | 41 15 * * * | insert into t1(curr_time) values(now()::varchar);               | localhost |     5432 | db_test  | scott    | t      | 
     2 | 52 15 * * * | insert into t1(curr_time) values(now()::varchar);               | localhost |     5432 | db_test  | scott    | t      | 
     3 | 56 15 * * * | insert into t1(curr_time,curr_user) values(now()::varchar,user) | localhost |     5432 | db_test  | scott    | t      | 
     4 | 59 15 * * * | insert into t1(curr_time,curr_user) values(now()::varchar,user) | localhost |     5432 | db_test  | scott    | t      | 
     6 | 10 seconds  | insert into t1(curr_time,curr_user) values(now()::varchar,user) | localhost |     5432 | db_test  | scott    | t      | Ever 10s insert data
(5 rows)
 

3.3 查询job执行日志

select * from cron.job_run_details order by start_time desc ;

db_test=> select * from cron.job_run_details order by start_time desc ;

jobid|runid|job_pid|database|username|command                                                        |status   |return_message   |start_time                   |end_time                     |
-----+-----+-------+--------+--------+---------------------------------------------------------------+---------+-----------------+-----------------------------+-----------------------------+
    3|    1|       |db_test |scott   |insert into t1(curr_time,curr_user) values(now()::varchar,user)|failed   |connection failed|                             |                             |
    4|    2|   3880|db_test |scott   |insert into t1(curr_time,curr_user) values(now()::varchar,user)|succeeded|INSERT 0 1       |2023-03-21 15:59:00.017 +0800|2023-03-21 15:59:00.018 +0800|
(2 rows)

return_message:返回的结果,发现第一个是因为连接失败了,之后可以结合pg日志检查免密认证的情况

3.4 修改job

语法:

SELECT cron.alter_job('<定时任务ID>, '<定时计划>', '<定时任务>', '<执行数据库>', '<执行用户>', '<任务是否启用>');

遇到问题:发现普通用户即使能够创建和删除,但是也不能修改job,应该是个BUG?

db_test=> select cron.alter_job('6','15 seconds',$$insert into t1(curr_time,curr_user) values(now()::varchar,user)$$);
ERROR:  permission denied for function alter_job

3.5 删除job

语法:

SELECT cron.unschedule(<定时任务ID>);
SELECT cron.unschedule('<定时任务名称>');
此处删除job,普通用户就能执行成功了
db_test=> SELECT cron.unschedule(6);
 unschedule 
------------
 t
(1 row)

四 对pg_cron并发执行的探索

思路:

job1和job2,设置相同的任务时间,同时对测试表进行写入,

job3执行pg_sleep(180),目的是测试阻挡job1和job2

1. 创建1张表,

create table t2 (id serial,curr_time varchar,curr_user varchar,job_name varchar);

2. 创建3个job

job1和job2增加pg_sleep(3)是为了区分开2个不同的任务设定到同一时间后,是否也会相互等待

job3设置180s,是为了验证同一个job未执行完,后面该job又到执行时间的情况,能否直接执行

SELECT cron.schedule('job1','*/2 * * * *', $$insert into t2(curr_time,curr_user,job_name) values(now()::varchar,user,'job1');select pg_sleep(3)$$);

SELECT cron.schedule('job2','*/2 * * * *', $$insert into t2(curr_time,curr_user,job_name) values(now()::varchar,user,'job2');select pg_sleep(3)$$);

SELECT cron.schedule('job3','*/2 * * * *', $$insert into t2(curr_time,curr_user,job_name) values(now()::varchar,user,'job3');select pg_sleep(180)$$);

3.等待多执行几次,验证结论


db_test=> select * from t2;
 id |           curr_time           | curr_user | job_name 
----+-------------------------------+-----------+----------
 18 | 2023-03-21 16:54:00.018432+08 | scott     | job1
 19 | 2023-03-21 16:54:00.034451+08 | scott     | job2
 20 | 2023-03-21 16:54:00.035248+08 | scott     | job3
 21 | 2023-03-21 16:56:00.007111+08 | scott     | job1
 22 | 2023-03-21 16:56:00.012271+08 | scott     | job2
 23 | 2023-03-21 16:57:00.098638+08 | scott     | job3      --本来是偶数分钟执行,现在排到16:57了,正好距离第一次执行差180s
 24 | 2023-03-21 16:58:00.019738+08 | scott     | job1
 25 | 2023-03-21 16:58:00.031114+08 | scott     | job2
 26 | 2023-03-21 17:00:00.016945+08 | scott     | job1
 27 | 2023-03-21 17:00:00.026808+08 | scott     | job2
 28 | 2023-03-21 17:00:00.188775+08 | scott     | job3
 29 | 2023-03-21 17:02:00.014714+08 | scott     | job1
 30 | 2023-03-21 17:02:00.025062+08 | scott     | job2
 32 | 2023-03-21 17:04:00.016704+08 | scott     | job1
 33 | 2023-03-21 17:04:00.027617+08 | scott     | job2
(15 rows)

验证结论:

对不同的job,同一时间点是可以并发执行的

对于同一个job,假如该job第一次还未执行完,就到第二次执行的时间了,那么第二次执行就会等待第一次执行完成后才能执行.

  • 0
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值