数据库审计概述
数据库审计,说白了就是给数据库装个“黑匣子”。谁来过、干了啥、动了哪些表,全得记下来。我刚开始做运维那会儿,总觉得审计日志是累赘——占磁盘、拖性能,直到有一次线上数据被误删,追查了三天没头绪……嗯,后来我才明白,没有审计日志的数据库就是裸奔。
审计日志是干什么的?
审计日志的核心作用就三个:追溯、合规、预警。
- 追溯:出问题了,能定位到人、时间、操作语句。比如谁在凌晨执行了
DROP TABLE。 - 合规:等保2.0、SOX、PCI-DSS都要求数据库有完整审计记录。你不记,检查就过不了。
- 预警:实时分析审计日志,发现异常立马告警。比如短时间内大量失败的登录尝试。
我个人习惯,生产库至少保留90天审计日志。太长影响查询性能,太短出了事查不到。你想想看,一个核心交易系统,每天几百万条审计记录,三个月的数据量确实不小,但跟业务损失比起来,这点存储成本不值一提。
一句话记住:审计日志不是为了满足好奇心,而是为了在出问题时有据可查。
审计策略怎么配?
配置审计策略,其实是回答三个问题:谁?什么操作?什么时候记?
以 MySQL 8.0 的审计插件为例,我常用的配置模板长这样:
-- 开启审计插件
INSTALL PLUGIN audit_log SONAME 'audit_log.so';
SET GLOBAL audit_log_policy = 'ALL';
SET GLOBAL audit_log_format = 'JSON';
SET GLOBAL audit_log_rotate_on_size = 104857600; -- 100MB轮转
SET GLOBAL audit_log_rotations = 10;
这段配置的作用是:记录所有操作、输出 JSON 格式方便解析、每 100MB 自动轮转、保留最近 10 个文件。我在项目中遇到过,有人把 audit_log_policy 设为 NONE 然后忘了开,审计日志为空。避坑指南:配置完一定要用 SELECT * FROM performance_schema.audit_log 验证是否真的在记录。
不同数据库的审计策略差异很大,我整理了一张对比表:
| 数据库类型 | 启用方式 | 策略粒度 | 典型配置路径 |
|---|---|---|---|
| MySQL 8.0+ | 插件 audit_log.so | 全局/按用户 | SET GLOBAL audit_log_policy='ALL' |
| Oracle | 系统包 DBMS_AUDIT | 按语句/按对象 | AUDIT SELECT TABLE BY hr BY ACCESS; |
| PostgreSQL | 扩展 pgaudit | 按角色/按DDL/DML | shared_preload_libraries='pgaudit' |
| SQL Server | 服务器级审核 | 按审核规范 | CREATE SERVER AUDIT ... |
配置时有个坑:审计策略太粗会漏,太细则炸磁盘。我曾经给一个高并发系统配了“ALL”级别,结果每天产生 200GB 审计日志,直接把磁盘怼爆了。后来改成了只审计 DDL 和登录失败事件,日志量降到了每天 2GB。
我的经验:先按最细的级别跑一周,观察日志增长速率,然后去掉那些“没有价值”的查询。比如 SELECT 1 这种健康检查,完全可以过滤掉。
三种核心审计类型
日常运维中,我遇到最多的审计类型就三种:登录审计、DDL审计、DML审计。三者的关注点完全不同。
| 审计类型 | 记录什么 | 典型风险 | 关键字段 |
|---|---|---|---|
| 登录审计 | 谁、从哪、何时登录/登出 | 暴力破解、账号共享 | USER, HOST, LOGIN_TIME, LOGOUT_TIME |
| DDL审计 | CREATE/ALTER/DROP/TABLE/INDEX等 | 恶意删表、改结构 | OBJECT, STATEMENT, SQLTEXT |
| DML审计 | SELECT/INSERT/UPDATE/DELETE | 数据泄露、拖库 | ROWS_AFFECTED, SQLTEXT, DURATION |
登录审计
最基础也最重要。我见过一家公司,数据库账号密码写在代码配置文件里,一直没人换,结果被扫出来了。登录审计能第一时间发现异常登录行为——比如凌晨3点从境外IP登录。配置示例(Oracle):
AUDIT CREATE SESSION;
AUDIT CREATE SESSION WHENEVER NOT SUCCESSFUL;
第一条记录所有登录,第二条只记录失败的。我个人习惯同时开启:成功日志用于正常溯源,失败日志用于发现扫描器。
DDL审计
结构变更风险极高。曾经一个实习生在生产库上直接执行了 DROP TABLE orders,还好 DDL 审计记录到了他的IP和操作时间,10分钟就定位到人了。DDL 审计的配置方向:
- MySQL:
SET GLOBAL audit_log_policy = 'DDL'; - PostgreSQL:
pgaudit.log = 'DDL'; - SQL Server: 在服务器审核规范中添加
SCHEMA_OBJECT_CHANGE_GROUP
注意:DDL 审计通常是“必开”项,因为结构变更不可逆。除非你的数据库是只读库,否则建议一直开着。
DML审计
DML 审计最容易产生海量日志。尤其是 SELECT 语句,一个报表查询可能几百万行,审计日志量能超过业务数据本身。我的建议是:只审计敏感表的 DML,比如包含身份证、银行卡号的列。以 MySQL 审计插件为例,可以按表过滤:
SET GLOBAL audit_log_include_accounts = 'app_user@%';
SET GLOBAL audit_log_include_tables = 'user_info, payment_log';
这样只有指定用户操作指定表时才会记录,日志量可控。我曾遇到一个案例:某系统全量审计 DML,导致数据库 IO 飙升到 99%,业务响应时间从 10ms 变成 10s。后来改成只审计 DELETE 和 UPDATE(不审计 SELECT),问题瞬间解决。
为什么我要用这张图?因为很多新手只知道开了审计,却不理解数据流。你看整个链路:应用客户端发SQL → 数据库服务器处理 → 审计引擎根据策略决定记不记 → 最后落地到日志文件。你想排查异常,就得从日志出发,逆向回推:谁?哪个IP?执行了什么语句?只有理解了这条链条,分析时才有方向。
实战要点:审计日志的“源头”是审计引擎,不是数据库自己凭空生成的。所以排查异常时,第一步不是翻日志,而是确认审计引擎是否正常运行——比如进程是否存活、磁盘是否满写。