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 负载。

相关推荐
这个DBA有点耶1 小时前
Oracle 迁移金仓兼容性评估指南:5 大维度深度拆解
数据库·oracle·架构
添砖java‘’3 小时前
Redis中常用数据结构
数据库·redis·缓存
用户3169353811833 小时前
MyBatis Mapper XML 文件解读
数据库
这个DBA有点耶3 小时前
数据库迁移灰度验证:从读流量到双写再到全量切换的完整路径
数据库·mysql·架构
Wang's Blog3 小时前
PostgreSQL笔记16: 索引基础——概念、类型、创建与最佳实践
数据库·笔记·postgresql
Suhan424 小时前
SQLAlchemy的两种查询query()查询、stmt=select()查询
数据库·python·sql·oracle
用户3169353811834 小时前
"批量"删除-sql
数据库
瀚高PG实验室4 小时前
SQL优化案例:使用分区表优化SQL查询性能
linux·服务器·数据库·sql·microsoft·postgresql
SelectDB4 小时前
15 分钟搭建 PostgreSQL + Apache Iceberg + Apache Doris 的湖仓分析平台
数据库