Postgresql触发器实现对表的日志审计

1. 创建日志表与触发器函数

复制代码
-- 1. 创建通用日志表
CREATE TABLE t_case_log (
    log_id BIGSERIAL PRIMARY KEY,
    case_id BIGINT,
    operation VARCHAR(10), -- INSERT, UPDATE, DELETE
    old_data JSONB,        -- 修改/删除前的数据
    new_data JSONB,        -- 新增/修改后的数据
    changed_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    changed_by VARCHAR(50) DEFAULT CURRENT_USER
);

-- 2. 创建触发器函数
CREATE OR REPLACE FUNCTION process_t_case_audit()
RETURNS TRIGGER AS $$
BEGIN
    IF (TG_OP = 'DELETE') THEN
        INSERT INTO t_case_log(case_id, operation, old_data)
        VALUES (OLD.id, 'DELETE', to_jsonb(OLD));
        RETURN OLD;
    ELSIF (TG_OP = 'UPDATE') THEN
        INSERT INTO t_case_log(case_id, operation, old_data, new_data)
        VALUES (NEW.id, 'UPDATE', to_jsonb(OLD), to_jsonb(NEW));
        RETURN NEW;
    ELSIF (TG_OP = 'INSERT') THEN
        INSERT INTO t_case_log(case_id, operation, new_data)
        VALUES (NEW.id, 'INSERT', to_jsonb(NEW));
        RETURN NEW;
    END IF;
    RETURN NULL;
END;
$$ LANGUAGE plpgsql;

-- 3. 将触发器绑定到 t_case 表
CREATE TRIGGER t_case_audit_trigger
AFTER INSERT OR UPDATE OR DELETE ON t_case
FOR EACH ROW EXECUTE FUNCTION process_t_case_audit();

2. 查询审计日志

直接在 PostgreSQL 执行 SQL 或通过 Spring Data JPA 映射 t_case_log 进行查询:

复制代码
SELECT * FROM t_case_log WHERE case_id = 1001 ORDER BY changed_at DESC;

这段 SQL 脚本实现了一个数据库层面的自动数据审计机制 。当对 t_case 表执行新增、修改或删除(CUD)时,PostgreSQL 会自动捕获数据变动,并将变更前后的完整快照以 JSON 格式写入 t_case_log 日志表。

一、 审计日志表设计 (t_case_log)

  • log_id :日志主键,使用 BIGSERIAL 实现自增。

  • case_id:关联的目标记录 ID。

  • operation :记录操作类型,仅包含 INSERTUPDATEDELETE 三种。

  • old_data / new_data :核心设计,采用 PostgreSQL 的 JSONB 类型。直接将整行数据转为 JSON 存储,优势在于即便将来 t_case 表增删字段,该日志表结构也无需任何修改

  • changed_at / changed_by :利用数据库默认值(CURRENT_TIMESTAMPCURRENT_USER)自动记录变动发生的时间与数据库操作账号。

二、 触发器函数逻辑 (process_t_case_audit)

函数基于 PL/pgSQL 编写,利用了 PostgreSQL 的内置触发器变量:

  • 内置变量解析

    • TG_OP :存储触发当前函数的 SQL 类型(INSERT / UPDATE / DELETE)。

    • OLD :代表变更或删除 的整行记录(仅在 UPDATEDELETE 时可用)。

    • NEW :代表新增或修改 的整行记录(仅在 INSERTUPDATE 时可用)。

    • to_jsonb(...):PG 内置函数,直接将一行 Row 记录转换为 JSONB 对象。

  • 分流逻辑

    • DELETE :仅提取并记录 OLD 数据(删除前的快照)。

    • UPDATE :同时记录 OLD(修改前)和 NEW(修改后),方便后期做属性级别的差异对比(Diff)。

    • INSERT :仅提取并记录 NEW 数据(新插入的数据)。

三、 绑定触发器 (t_case_audit_trigger)

  • AFTER INSERT OR UPDATE OR DELETE :采用 事后触发(AFTER) 。只有在 t_case 的数据修改成功后才会写入日志;若主语句因约束等原因报错回滚,日志写入也会同步回滚,确保审计数据的绝对真实性。

  • FOR EACH ROW:行级触发器。如果是批量修改(例如一条 SQL 更新了 10 条记录),函数会被调用 10 次,生成 10 条精准的日志。

💡 实际落地时的注意事项

  1. 应用层操作人问题changed_by 默认记录的是 PG 的数据库连接用户(如 postgres)。如果是 Spring Boot 应用通过连接池操作,这里记录的永远是连接池账号,无法体现"系统登录的具体用户" 。如果需要记录业务用户 ID,通常需要配合 PG 的临时变量(SET LOCAL app.current_user_id = 'xxx')或在应用层(如 Hibernate Envers / AOP)处理。

  2. 写性能开销 : 由于是 AFTER ... FOR EACH ROW 触发器,每一次对 t_case 的写入都会同步带来一次对 t_case_log 的写入(1:1 写放大)。在大批量数据导入或高并发写入场景下,需注意评估 IO 负载。

相关推荐
2601_9622838812 分钟前
Django数据库配置(一)
mysql·postgresql·django·sqlite·数据库配置
落木萧萧82515 分钟前
MyBatis 启动的时候都在干什么:从 MappedStatement 说起
java·数据库·后端
g105655913928 分钟前
华为 OceanStor 基础使用入门指南
服务器·数据库·性能优化
潇凝子潇28 分钟前
MySQL buffer pool 计算公式
数据库·mysql
广州灵眸科技有限公司42 分钟前
瑞芯微(EASY EAI)RV1126B display
开发语言·数据库·人工智能·科技·嵌入式硬件
AugustSkys1 小时前
PostgreSQL 完全指南:特性对比 + 安装部署 + 配置管理 + psql 实战 + pgAdmin 图形化
运维·postgresql·开源数据库·pgadmin
凌晨1681 小时前
MySQL:DML、DDL与TCL精讲
数据库·mysql·oracle
风哥2号1 小时前
数据库教程FGMT04‑生产环境Linux+Oracle19c RAC集群安装配置与项目实战
linux·数据库
Java开发追求者1 小时前
navicat连接新的数据库提示Oracle library is not loaded.
数据库·oracle·oracle11g·oracle library·is not loaded
砚底藏山河1 小时前
存储选型实战:CSV-SQLite-MySQL同机基准(魔码量化实战 #02)
java·数据库·python·金融·maven