必须用数据库级系统触发器,由SYS或具备ADMINISTER DATABASE TRIGGER权限的用户创建,仅支持AFTER STARTUP和BEFORE SHUTDOWN事件,作用域必须为ON DATABASE,日志表需预建并确保SYS可写,禁止COMMIT、DBMS_OUTPUT及动态视图查询。

必须用数据库级系统触发器,且需 SYS 或具备 ADMINISTER DATABASE TRIGGER 权限的用户创建,普通用户无权监听数据库启停事件。
数据库启动与关闭触发器必须定义为 DATABASE 级别
只有 AFTER STARTUP 和 BEFORE SHUTDOWN 这两个事件支持在数据库级触发器中使用,且触发器作用域必须是 ON DATABASE,不能写成 ON SCHEMA 或省略作用域。常见错误是误用 ON SCHEMA 导致触发器根本不会被调用。
-
AFTER STARTUP:数据库实例完成启动、所有后台进程就绪后触发,此时数据字典已可访问 -
BEFORE SHUTDOWN:数据库开始关闭流程前触发,仍可执行 DML 和查询,但不可发起新事务(因关闭流程会阻塞新事务) - 不能用
BEFORE STARTUP或AFTER SHUTDOWN—— Oracle 不支持这两个事件 - 触发器体中禁止调用
DBMS_OUTPUT.PUT_LINE,因为数据库启停时没有客户端会话接收输出
日志表必须预建且对 SYS 可写
记录启停事件需要写入一张物理表,比如 db_event_log,它必须由 SYS 用户创建或授权给触发器所在用户(通常是 SYS),否则会报 ORA-00604: error occurred at recursive SQL level 1。
- 建表语句示例:
CREATE TABLE db_event_log (event_type VARCHAR2(20), event_time DATE, instance_name VARCHAR2(64)) - 必须确保该表所在表空间在线、有足够空间,否则
INSERT失败会导致AFTER STARTUP触发器静默失败(不报错但不写日志) - 避免在触发器里用
SELECT ... INTO查询动态性能视图(如V$INSTANCE),某些版本中启停初期这些视图可能尚未完全可用;稳妥做法是直接用SYS_CONTEXT('USERENV', 'INSTANCE_NAME')
权限与部署必须由 DBA 在 SYS 下完成
普通开发账号即使有 CREATE TRIGGER 权限,也无法创建数据库级触发器。真正能生效的部署路径只有一条:
- 以
SYS AS SYSDBA登录(不是普通SYS,必须带SYSDBA) - 执行
GRANT ADMINISTER DATABASE TRIGGER TO your_user(若需他人维护,但通常不建议) - 触发器本身必须显式声明为
CREATE OR REPLACE TRIGGER tg_db_lifecycle AFTER STARTUP OR BEFORE SHUTDOWN ON DATABASE - 触发器内所有 DML 操作(如
INSERT INTO db_event_log)都运行在SYS上下文,所以目标表必须对SYS可写,不能依赖角色权限(角色在系统触发器中不生效)
常见失败场景和静默陷阱
这类触发器最容易“看似正常却从不记录”,问题往往不出在语法,而在上下文权限和环境状态。
- 在测试库用
SHUTDOWN IMMEDIATE; STARTUP;验证时,如果触发器里写了INSERT但日志表没授权给SYS,Oracle 不抛错也不提示,只是跳过执行 —— 查V$TRIGGER的STATUS是ENABLED,但查日志表为空 - 误在触发器中调用
COMMIT:系统触发器运行在 Oracle 自动开启的隐式事务中,显式COMMIT会报ORA-04092: cannot COMMIT in a trigger - 用
CURRENT_DATE记时间:启停过程可能跨时区切换,应统一用SYSDATE - 试图在
BEFORE SHUTDOWN中做耗时操作(如发邮件、调外部程序):会拖慢关闭流程,甚至导致实例无法正常关闭
最易被忽略的一点:数据库启停事件触发器只在实例级别生效,RAC 环境下每个节点独立触发,不会跨实例聚合;若需全局视图,日志表必须建在共享表空间,且各节点插入时需带上 INSTANCE_NUMBER 字段区分来源。


















