前言
这篇文章探讨了pgrx框架,它简化了创建自定义PostgreSQL扩展的过程,将更多的逻辑靠近数据库。传统上,编写此类扩展需要熟悉C语言以及对PostgreSQL内部机制的深入理解,这可能相当具有挑战性。pgrx降低了门槛,可以让开发者使用以安全性和性能著称的Rust语言,使创建高效且安全的数据库扩展过程更加容易。
pg_sysload
在处理大型数据集和迁移或在资源密集型的维护任务期间,您会希望优化速度并尽量减少对其他进程的干扰。控制批处理操作节奏的一种方法是考虑底层系统的负载。
许多基于Unix的系统(我们将重点关注Linux)提供了一个非常有价值的指标,称为系统负载平均值。这个平均值由三个值组成:1分钟、5分钟和15分钟的负载平均值。负载平均值没有针对CPU核心数进行标准化,因此在单核系统上,1的负载平均值意味着完全利用,而在四核系统上,则表示25%的利用。
在许多情况下,系统负载平均值也是衡量正在进行的操作对繁忙数据库集群影响的一个很好的指标。在本文中,我们将创建一个名为sys_loadavg()的PostgreSQL扩展函数,用于检索此负载信息。我们将使用/proc/loadavg文件(proc文件系统的一部分),它公开了底层系统的细节。
开始使用pgrx
在我们开始之前,请确保您已经安装了:
1.Rust(开始使用Rust)
2.为pgrx安装了系统依赖项
有了这些先决条件,您可以安装pgrx本身并创建一个新的扩展骨架:
cargo install --locked cargo-pgrx
cargo pgrx new pg_sysload
cd pg_sysload
这为您提供了一个完整的环境,用于使用Rust开发您自己的PostgreSQL扩展。虽然Rust可能一开始看起来令人生畏(特别是它的异步和其他特性),但该语言本身非常强大。它在扩展创建方面的便捷性值得您再次关注。
设置扩展
虽然pgrx new命令设置了一个基本模板,我们将创建一个更复杂的扩展。其主要逻辑涉及解析/proc/loadavg文件,提取其值,并将它们返回给用户。
逻辑本身很直接:
#[pg_extern]
fn sys_loadavg() -> Option<Vec<f64>> {
// 读取/proc/loadavg的内容
let mut file = match fs::File::open("/proc/loadavg") {
Ok(file) => file,
Err(err) => {
pgrx::error!("读取/proc/loadavg时出错:{}", err);
}
};
let mut contents = String::new();
if let Err(err) = file.read_to_string(&mut contents) {
pgrx::error!("读取/proc/loadavg时出错:{}", err);
}
// 提取负载平均字段
let fields = contents.split_whitespace().collect::<Vec<_>>();
if fields.len() >= 3 {
Some(
fields[..3]
.iter()
.filter_map(|s| f64::from_str(s).ok())
.collect(),
)
} else {
pgrx::error!("在/proc/loadavg中的格式无效");
}
}
为了限制仅在支持proc文件系统的系统上使用,我们在扩展加载时检查文件的存在:
#[pg_guard]
fn _PG_init() {
INIT.call_once(|| {
let loadavg_available = fs::metadata("/proc/loadavg").is_ok();
if !loadavg_available {
pgrx::error!("/proc/loadavg未找到。扩展无法加载。");
}
});
}
这个基本检查可以防止用户在不兼容的系统上加载扩展。省略了一些细节,这就是几乎所有的内容。您可以在附带的GitHub存储库中找到完整的lib.rs文件。
运行扩展
pgrx真正闪耀之处在于它为您做了所有的重活。它利用了cargo命令。要初始化扩展,请运行:
cargo pgrx init
这将下载并准备PostgreSQL的源代码,版本为12到16(在撰写本文时)。经过一段时间的等待(希望成功,如果您拥有所有系统依赖项),运行扩展:
cargo pgrx run
您将获得一个专用PostgreSQL实例的psql提示。创建并使用扩展:
CREATE EXTENSION pg_sysload;
现在,您可以尝试新公开的函数:
SELECT sys_loadavg();
sys_loadavg
------------------
{1.17, 0.57, 0.32}
(1 row)
就这样。您的第一个PostgreSQL扩展正在工作。
节流批处理
有了扩展,让我们重新审视我们最初的目标:节流长时间运行的批数据处理。来自sys_loadavg的数据可以以多种方式使用:
动态睡眠时间: 根据系统负载插入计算出的睡眠时间间隔。
动态批量大小: 根据可用资源调整每批处理的行数。
这里有一个例子:
#堆代码 duidaima.com
-- 根据系统的1分钟负载乘以5秒休眠
SELECT pg_sleep((sys_loadavg())[1] * 5);
-- 假设有20个核心,每个可用的核心,增加100行进行处理
SELECT ... LIMIT (SELECT (20 - (sys_loadavg())[1])::int * 100);
这使您可以为无人监督的操作微调处理,自动适应当前的系统负载。
结论
就这样!您刚刚使用Rust和pgrx框架构建了您的第一个PostgreSQL扩展。我一直觉得编写扩展的前景相当令人畏惧(可能是因为我早已离开了C生态系统),但这并不糟糕。您现在拥有了一个方便的工具来监控系统负载,非常适合监控您的数据库如何处理长时间运行的迁移。
这仅仅是冰山一角。我们甚至还没有触及到真正有趣的东西——通过自定义数据类型、运算符或索引扩展PostgreSQL的内部。您可以构建扩展来转换或聚合数据,连接到外部API,或创建后台工作者,将各种业务逻辑直接带入数据库引擎。是的,利用数据库的核心可能有点冒险,评估风险和恢复选项始终很重要。但无论您选择创建什么,请记住,有了pgrx的支持,您拥有Rust的力量来保持事物的安全和稳定。