citusdata/pg_cron
GitHub: citusdata/pg_cron
pg_cron 是一个运行在 PostgreSQL 内部的定时任务调度扩展,让用户可以用标准 cron 语法直接在数据库中创建和管理周期性 SQL 作业。
Stars: 3775 | Forks: 249
# 什么是 pg_cron?
pg_cron 是一个基于 cron 的简单作业调度器,适用于 PostgreSQL(10 或更高版本),它作为扩展在数据库内部运行。
# 目录
- [pg_cron 的工作原理](#how-pg_cron-works)
- [Cron 语法](#cron-syntax)
- [管理和创建作业](#managing-and-creating-jobs)
- [创建 cron 作业](#creating-a-cron-job)
- [在不同数据库中创建 cron 作业](#creating-a-cron-job-in-a-different-database)
- [删除 cron 作业](#removing-a-cron-job)
- [修改 cron 作业](#altering-a-cron-job)
- [安装 pg_cron](#installing-pg_cron)
- [设置 pg_cron](#setting-up-pg_cron)
- [监控作业](#monitoring-jobs)
- [示例用例](#example-use-cases)
- [托管服务](#managed-services)
- [行为准则](#code-of-conduct)
# pg_cron 的工作原理
该扩展会创建一个后台工作进程,用于跟踪 `cron.job` 表中的作业。
```
CREATE TABLE cron.job (
jobid bigint primary key default pg_catalog.nextval('cron.jobid_seq'),
schedule text not null,
command text not null,
nodename text not null default 'localhost',
nodeport int not null default pg_catalog.inet_server_port(),
database text not null default pg_catalog.current_database(),
username text not null default current_user
);
```
根据您的配置,为了执行作业,该扩展会建立一个 Postgres 连接或生成一个数据库工作进程。
pg_cron 可以并行运行多个作业,但每个特定作业一次只能运行一个实例。如果在第一个实例完成之前触发了第二个实例,它将被排队,并在第一个实例完成后立即启动。
# Cron 语法
pg_cron 中处理解析和调度的代码直接来源于 [Paul Vixie 的 cron 源码](https://github.com/vixie/cron),因此支持相同的选项。
```
┌───────────── min (0 - 59)
│ ┌────────────── hour (0 - 23)
│ │ ┌─────────────── day of month (1 - 31) or last day of the month ($)
│ │ │ ┌──────────────── month (1 - 12)
│ │ │ │ ┌───────────────── day of week (0 - 6) (0 to 6 are Sunday to
│ │ │ │ │ Saturday, or use names; 7 is also Sunday)
│ │ │ │ │
│ │ │ │ │
* * * * *
```
创建 cron 调度的一种简单方法是使用:[crontab.guru](http://crontab.guru/)。
pg_cron 还允许您:
- 使用 `$` 表示当月的最后一天。
- 使用 `[1-59] seconds` 根据时间间隔来调度作业。请注意,不能将秒数与其他时间单位结合使用。
cron 调度示例:
```
'10 seconds' # every 10 seconds
* * * * * # every minute
*/5 * * * * # every 5 minutes
0 * * * * # every hour
0 0 * * * # daily at 12AM
0 0 * * 1-5 # 12AM every weekday
0 1 * * 0 # 1AM every Sunday
0 13 2 6 * # 1PM on the 2nd of June
```
# 管理和创建作业
如果您拥有所需的权限,可以通过直接与 `cron.job` 表交互来管理 cron 作业。但是,建议使用 cron 函数:
- [`cron.schedule`](#creating-a-cron-job)
- [`cron.schedule_in_database`](#creating-a-cron-job-in-a-different-database)
- [`cron.unschedule`](#removing-a-cron-job)
- [`cron.alter_job`](#altering-a-cron-job)
### 创建 cron 作业
#### `cron.schedule` 签名
```
-- create job, return jobid
CREATE OR REPLACE FUNCTION cron.schedule(schedule text, command text)
RETURNS bigint;
-- create named job, return jobid
CREATE OR REPLACE FUNCTION cron.schedule(job_name text, schedule text, command text)
RETURNS bigint
```
#### 示例
##### 创建一个 cron 作业
```
-- Delete old data on Saturday at 3:30am (GMT)
SELECT cron.schedule(
'30 3 * * 6',
$$DELETE FROM events WHERE event_time < now() - interval '1 week'$$
);
-- returns cron id
```
##### 创建一个命名的 cron 作业
```
-- Vacuum every day at 10:00am (GMT)
SELECT cron.schedule(
'nightly-vacuum',
'0 10 * * *',
'VACUUM'
);
-- returns cron id
```
##### 创建一个每 30 秒运行一次的作业
```
-- run SELECT 1 every 30 seconds
SELECT cron.schedule(
'run_every_30_seconds',
'30 seconds',
'SELECT 1'
);
-- returns cron id
```
##### 创建一个每 5 秒调用一次存储过程的作业
```
-- Call a stored procedure every 5 seconds
SELECT cron.schedule(
'process-updates',
'5 seconds',
'CALL process_updates()'
);
-- returns cron id
```
##### 创建一个在每月最后一天 12:00 处理工资单的作业
```
-- Process payroll at 12:00 of the last day of each month
SELECT cron.schedule(
'process-payroll',
'0 12 $ * *',
'CALL process_payroll()'
);
-- returns cron id
```
### 在不同数据库中创建 cron 作业
#### `cron.schedule_in_database` 签名
```
-- create job, return jobid
CREATE OR REPLACE FUNCTION cron.schedule_in_database(
job_name text,
schedule text,
command text,
database text,
username text DEFAULT NULL::text,
active boolean DEFAULT true
)
RETURNS bigint
```
#### 示例
##### 在不同数据库中创建 cron 作业
```
-- Delete old data on Saturday at 3:30am (GMT)
SELECT cron.schedule_in_database(
'delete_old_data',
'30 3 * * 6',
$$DELETE FROM events WHERE event_time < now() - interval '1 week'$$,
'some_other_database'
);
-- returns cron id
```
### 删除 cron 作业
#### `cron.unschedule` 签名
```
-- remove job by name, return true if job was removed
CREATE OR REPLACE FUNCTION cron.unschedule(job_name text)
RETURNS boolean
-- remove job by id, return true if job was removed
CREATE OR REPLACE FUNCTION cron.unschedule(job_id bigint)
RETURNS boolean
```
#### 示例
##### 删除一个命名的 cron 作业
```
-- delete job by name
SELECT cron.unschedule('nightly-vacuum');
-- returns true if job was removed
```
##### 通过 id 删除 cron 作业
```
-- delete job by id
SELECT cron.unschedule(42);
-- returns true if job was removed
```
### 修改 cron 作业
#### `cron.alter_job` 签名
```
CREATE OR REPLACE FUNCTION cron.alter_job(
job_id bigint,
schedule text DEFAULT NULL::text,
command text DEFAULT NULL::text,
database text DEFAULT NULL::text,
username text DEFAULT NULL::text,
active boolean DEFAULT NULL::boolean
)
RETURNS void
```
#### 示例
##### 更改作业的调度计划
```
-- change job's schedule
SELECT cron.alter_job(42, '0 10 * * *');
-- returns void
```
##### 更改作业的调度计划、命令和用户名
```
-- change job's command
SELECT cron.alter_job(
42,
'0 10 * * *',
'VACUUM',
username := 'some_other_user'
);
-- returns void
```
##### 停用一个作业
```
-- deactivate job
SELECT cron.alter_job(42, active := false);
-- returns void
```
# 安装 pg_cron
在 Red Hat、CentOS、Fedora、Amazon Linux 上使用 [PGDG](https://yum.postgresql.org/repopackages/) 安装适用于 PostgreSQL 18 的版本:
```
# 安装 pg_cron 扩展
sudo yum install -y pg_cron_18
```
在 Debian、Ubuntu 上使用 [apt.postgresql.org](https://wiki.postgresql.org/wiki/Apt) 安装适用于 PostgreSQL 18 的版本:
```
# 安装 pg_cron 扩展
sudo apt-get -y install postgresql-18-cron
```
您也可以通过从源码构建来安装 pg_cron:
```
git clone https://github.com/citusdata/pg_cron.git
cd pg_cron
# 确保 pg_config 位于您的路径中,例如
export PATH=/usr/pgsql-18/bin:$PATH
make && sudo PATH=$PATH make install
```
# 设置 pg_cron
要启动 pg_cron 后台工作进程,您需要在 postgresql.conf 的 `shared_preload_libraries` 中添加 pg_cron。请注意,只要服务器处于[热备 (hot standby)](https://www.postgresql.org/docs/current/static/hot-standby.html) 模式,pg_cron 就不会运行任何作业,但它会在服务器被提升为主服务器后自动启动。
```
# 添加到 postgresql.conf
# 在启动时加载 pg_cron 后台工作进程所需
shared_preload_libraries = 'pg_cron'
```
默认情况下,pg_cron 后台工作进程期望其元数据表在 "postgres" 数据库中创建。但是,您可以通过在 postgresql.conf 中设置 `cron.database_name` 配置参数来进行更改。
```
# 添加到 postgresql.conf
# 可选,指定运行 pg_cron 后台工作进程的数据库(默认为 postgres)
cron.database_name = 'postgres'
```
`pg_cron` 只能安装到集群中的一个数据库中。如果您需要在多个数据库中运行作业,请使用 `cron.schedule_in_database()`。
以前 pg_cron 只能使用 GMT 时间,但现在您可以通过在 postgresql.conf 中设置 `cron.timezone` 来调整您的时间。
```
# 添加到 postgresql.conf
# 可选,指定运行 pg_cron 后台工作进程的时区(默认为 GMT)。例如:
cron.timezone = 'PRC'
```
重启 PostgreSQL 后,您可以使用 `CREATE EXTENSION pg_cron` 创建 pg_cron 函数和元数据表。
```
-- run as superuser:
CREATE EXTENSION pg_cron;
-- optionally, grant usage to regular users:
GRANT USAGE ON SCHEMA cron TO marco;
```
### 确保 pg_cron 能够启动作业
**重要提示**:默认情况下,pg_cron 使用 libpq 打开一个到本地数据库的新连接,这需要得到 [pg_hba.conf](https://www.postgresql.org/docs/current/static/auth-pg-hba-conf.html) 的允许。
对于运行 cron 作业的用户,可能需要为来自 localhost 的连接在 [pg_hba.conf](https://www.postgresql.org/docs/current/static/auth-pg-hba-conf.html) 中启用 `trust` 身份验证,或者您可以将密码添加到 [.pgpass 文件](https://www.postgresql.org/docs/current/static/libpq-pgpass.html)中,libpq 在打开连接时将使用该文件。
您还可以使用 unix 域套接字目录作为主机名,并在 [pg_hba.conf](https://www.postgresql.org/docs/current/static/auth-pg-hba-conf.html) 中为本地连接启用 `trust` 身份验证,这通常是安全的:
```
# 通过 unix 域套接字连接:
cron.host = '/tmp'
# 也可以是空字符串以查找默认目录:
cron.host = ''
```
或者,可以将 pg_cron 配置为使用后台工作进程。在这种情况下,并发作业的数量受 `max_worker_processes` 设置的限制,因此您可能需要提高该值。
```
# 通过后台工作进程而非 localhost 连接来调度作业
cron.use_background_workers = on
# 将可用的后台工作进程数量从默认的 8 个增加
max_worker_processes = 20
```
出于安全考虑,作业在调用 `cron.schedule` 函数的数据库中执行,并具有与当前用户相同的权限。此外,用户只能看到 `cron.job` 表中属于自己的作业。
```
-- View active jobs
select * from cron.job;
```
## 扩展设置
pg_cron 扩展支持以下配置参数:
| 设置 | 默认值 | 描述 |
| ---------------------------------| ----------- | ---------------------------------------------------------------------------------------- |
| `cron.database_name` | `postgres` | pg_cron 后台工作进程应在其中运行的数据库。 |
| `cron.enable_superuser_jobs` | `on` | 允许以超级用户身份调度作业。 |
| `cron.host` | `localhost` | 要连接的 postgres 主机名。 |
| `cron.launch_active_jobs` | `on` | 关闭时,停用所有活动作业,而无需重启服务器 |
| `cron.log_min_messages` | `WARNING` | 启动器后台工作进程的 log_min_messages。 |
| `cron.log_run` | `on` | 在 `cron.job_run_details` 表中记录所有运行的详细信息。 |
| `cron.log_statement` | `on` | 在执行前记录所有 cron 语句。 |
| `cron.max_running_jobs` | `32` | 可同时运行的最大作业数。 |
| `cron.timezone` | `GMT` | pg_cron 后台工作进程运行的时区。 |
| `cron.use_background_workers` | `off` | 使用后台工作进程而不是客户端连接。 |
### 更改设置
要查看设置配置,请运行:
```
SELECT * FROM pg_settings WHERE name LIKE 'cron.%';
```
可以在 postgresql.conf 文件中或使用以下命令更改设置:
```
ALTER SYSTEM SET cron. TO '';
```
`cron.log_min_messages` 和 `cron.launch_active_jobs` 具有 `sighup` 的[设置上下文 (setting context)](https://www.postgresql.org/docs/current/view-pg-settings.html#VIEW-PG-SETTINGS)。可以通过执行 `SELECT pg_reload_conf();` 来使它们生效。
所有其他设置都具有 postmaster 上下文,只有在服务器重启后才会生效。
# 监控作业
### 查看 `cron.job_run_details` 表
您可以在 `cron.job_run_details` 表中查看作业活动:
```
select * from cron.job_run_details order by start_time desc limit 5;
┌───────┬───────┬─────────┬──────────┬──────────┬───────────────────┬───────────┬──────────────────┬───────────────────────────────┬───────────────────────────────┐
│ jobid │ runid │ job_pid │ database │ username │ command │ status │ return_message │ start_time │ end_time │
├───────┼───────┼─────────┼──────────┼──────────┼───────────────────┼───────────┼──────────────────┼───────────────────────────────┼───────────────────────────────┤
│ 11 │ 4328 │ 2610 │ postgres │ marco │ select pg_sleep(3)│ running │ NULL │ 2023-02-07 09:30:00.098164+01 │ NULL │
│ 10 │ 4327 │ 2609 │ postgres │ marco │ select process() │ succeeded │ SELECT 1 │ 2023-02-07 09:29:00.015168+01 │ 2023-02-07 09:29:00.832308+01 │
│ 10 │ 4321 │ 2603 │ postgres │ marco │ select process() │ succeeded │ SELECT 1 │ 2023-02-07 09:28:00.011965+01 │ 2023-02-07 09:28:01.420901+01 │
│ 10 │ 4320 │ 2602 │ postgres │ marco │ select process() │ failed │ server restarted │ 2023-02-07 09:27:00.011833+01 │ 2023-02-07 09:27:00.72121+01 │
│ 9 │ 4320 │ 2602 │ postgres │ marco │ select do_stuff() │ failed │ job canceled │ 2023-02-07 09:26:00.011833+01 │ 2023-02-07 09:26:00.22121+01 │
└───────┴───────┴─────────┴──────────┴──────────┴───────────────────┴───────────┴──────────────────┴───────────────────────────────┴───────────────────────────────┘
(10 rows)
```
表中的记录不会自动清除,但每个有权限调度 cron 作业的用户也有权删除自己的 `cron.job_run_details` 记录。
特别是当您有每隔几秒运行一次的作业时,定期清理是个好习惯,这可以使用 pg_cron 本身轻松完成:
```
-- Delete old cron.job_run_details records of the current user every day at noon
SELECT cron.schedule('delete-job-run-details', '0 12 * * *', $$DELETE FROM cron.job_run_details WHERE end_time < now() - interval '7 days'$$);
```
如果您根本不想使用 `cron.job_run_details`,可以在 `postgresql.conf` 中添加 `cron.log_run = off`。
### 其他 cron 日志设置
如果配置了 `cron.log_statement` 设置,作业将在执行前被记录。`cron.log_min_messages` 设置控制将被记录的[最低消息级别](https://www.postgresql.org/docs/current/runtime-config-logging.html#RUNTIME-CONFIG-SEVERITY-LEVELS)。
# 示例用例
展示 pg_cron 可能的用法的文章:
* [使用 pg_partman 进行自动分区](https://www.citusdata.com/blog/2018/01/24/citus-and-pg-partman-creating-a-scalable-time-series-database-on-postgresql/)
* [在分析仪表板中计算汇总](https://www.citusdata.com/blog/2017/12/27/real-time-analytics-dashboards-with-citus/)
* [删除旧数据,执行 vacuum](https://www.citusdata.com/blog/2016/09/09/pgcron-run-periodic-jobs-in-postgres/)
* [喂猫](http://bonesmoses.org/2016/09/09/pg-phriday-irrelevant-inclinations/)
* [定期调用函数](https://fluca1978.github.io/2019/05/21/pgcron.html)
* [将 Postgres 作为 cron 服务器](https://supabase.io/blog/2021/03/05/postgres-as-a-cron-server)
# 托管服务
下表跟踪了主要的 Postgres 托管服务中哪些支持 pg_cron。
| 服务 | 是否支持 |
| ------------- |:-------------:|
| [Aiven](https://aiven.io/postgresql) | :heavy_check_mark: |
| [Alibaba Cloud](https://www.alibabacloud.com/help/doc-detail/150355.htm) | :heavy_check_mark: |
| [Amazon RDS](https://aws.amazon.com/rds/postgresql/) | :heavy_check_mark: | |
| [Azure](https://azure.microsoft.com/en-us/services/postgresql/) | :heavy_check_mark: |
| [Crunchy Bridge](https://www.crunchydata.com/products/crunchy-bridge/?ref=producthunt) | :heavy_check_mark: |
| [DigitalOcean](https://www.digitalocean.com/products/managed-databases/) | :heavy_check_mark: |
| [Google Cloud](https://cloud.google.com/sql/postgresql/) | :heavy_check_mark: |
| [Heroku](https://elements.heroku.com/addons/heroku-postgresql) | :x: |
| [Instaclustr](https://instaclustr.com) | :heavy_check_mark: |
| [Neon](https://neon.tech/docs/extensions/extensions-intro#tooling-admin) | :heavy_check_mark: |
| [PlanetScale](https://planetscale.com/docs/postgres/extensions) | :heavy_check_mark: |
| [ScaleGrid](https://scalegrid.io/postgresql.html) | :heavy_check_mark: |
| [Scaleway](https://www.scaleway.com/en/database/) | :heavy_check_mark: |
| [Supabase](https://supabase.io/docs/guides/database) | :heavy_check_mark: |
| [YugabyteDB](https://www.yugabyte.com/) | :heavy_check_mark: |
# 行为准则
本项目已采用 [Microsoft 开源行为准则](https://opensource.microsoft.com/codeofconduct/)。有关更多信息,请参阅[行为准则常见问题解答](https://opensource.microsoft.com/codeofconduct/faq/),或联系 [opencode@microsoft.com](mailto:opencode@microsoft.com) 提出任何其他问题或意见。
标签:cron, Cron语法, DNS解析, pg_cron, PostgreSQL, RDBMS, SQL, 任务调度, 作业调度, 后台任务, 定时任务, 定时脚本, 客户端加密, 并行执行, 开源项目, 插件, 数据库扩展, 数据库管理, 数据库维护, 数据清理, 测试用例, 系统审计, 网络调试, 自动化, 计划任务