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 :记录操作类型,仅包含 INSERT、UPDATE、DELETE 三种。

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

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

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

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

  • 内置变量解析:

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

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

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

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

相关推荐
瑞克-桑切斯2 小时前
企业生产环境:https部署+nginx高可用(业务不中断)
linux·服务器·数据库
zyseo810 小时前
谷歌SEO 准首页词挖掘脚本:找出排在第 8-20 名的隐藏流量
大数据·数据库·人工智能
xiaogai_gai12 小时前
仅查询策略实战:从金蝶云·星空旗舰版定时拉取收料通知单
大数据·数据库·python
数据工匠老o12 小时前
异构数据库DDL同步:260条链路的结构变更自动适配方案
数据库·架构
这个DBA有点耶13 小时前
银行业市占率最高的国产数据库是谁?先搞清楚“市占率”的四种口径
数据库·程序员·架构
用户62799471826214 小时前
南大通用GBase 8a之基于审计日志的冷温热数据分析
数据库
数据库小学妹14 小时前
数据库运维五级成熟度:监控、备份、高可用怎么串起来
运维·数据库·监控
一个天蝎座的程序猿14 小时前
为了给报表提速,我把金仓KES的列存储折腾了个遍:聊聊KES的技术能力
数据库
飞越未来云城的霓虹飞猪14 小时前
AI 数据库地图:从 PostgreSQL 到向量数据库,一次看懂 AI 时代的数据底座
数据库
不像程序员的程序媛14 小时前
导出mysql数据结构
数据库·mysql