【DB笔试面试449】如何监控数据库的登陆登出、DDL语句等内容?

  • 2019 年 10 月 11 日
  • 笔记

题目部分

如何监控数据库的登陆登出、DDL语句等内容?

答案部分

系统中一些常用的监控都可以使用DDL触发器和系统触发器来实现。可以先创建一张记录DDL语句的表XB_AUDIT_DDL_LHR(由于该表记录数会很大,所以,需创建成按月自动分区的分区表),并创建合适的索引,然后创建存储过程用于插入DDL信息到该日志表中。最后再创建系统触发器就可以将DDL语句或系统事件的信息插入日志表中。下面详细说明DDL触发器和系统触发器的使用。

首先创建一张记录DDL语句的表XB_AUDIT_DDL_LHR,由于该表记录数很大,所以,创建成按月自动分区的分区表,代码如下所示:

CREATE TABLE XB_AUDIT_DDL_LHR(    ID   NUMBER  PRIMARY KEY,    INST_ID NUMBER,    OPER_DATE     DATE,    OPERATION    VARCHAR2(30),    OBJECT_OWNER VARCHAR2(255),    OBJECT_TYPE  VARCHAR2(30),    OBJECT_NAME  VARCHAR2(30),    SQL_TEXT     VARCHAR2(4000),    SQL_FULLTEXT    CLOB,    OS_USER      VARCHAR2(255),    CLIENT_IP           VARCHAR2(20),    CLIENT_HOSTNAME     VARCHAR2(30),    DB_SCHEMA    VARCHAR2(30),    SID          NUMBER,    SERIAL#      NUMBER,    SPID         NUMBER,    SESSION_TYPE VARCHAR2(1000),    DATABASE_NAME VARCHAR2(255),    ERRORS_INFOR   VARCHAR2(4000),    SQL_ID  VARCHAR2(13),    PREV_SQL_ID VARCHAR2(13)    ) NOLOGGING  PARTITION BY RANGE(OPER_DATE)  INTERVAL(NUMTOYMINTERVAL(1,'MONTH'))  SUBPARTITION BY HASH(OPERATION)  SUBPARTITION TEMPLATE (    SUBPARTITION SP1 ,    SUBPARTITION SP2 ,    SUBPARTITION SP3)     (PARTITION P201610  VALUES LESS THAN(TO_DATE('201610','YYYYMM')));  ------创建主键列的用到的序列及其它常用列的索引  CREATE SEQUENCE S_XB_AUDIT_DDL_LHR START WITH 1 INCREMENT BY 1 CACHE 2000;  SELECT S_XB_AUDIT_DDL_LHR.NEXTVAL FROM DUAL;  CREATE INDEX IND_AUDIT_DDL_OBJECT_NAME ON   XB_AUDIT_DDL_LHR(OBJECT_NAME,OPERATION) LOCAL NOLOGGING ;  CREATE INDEX IND_AUDIT_DDL_OS_USER ON   XB_AUDIT_DDL_LHR(OS_USER) LOCAL NOLOGGING;  CREATE INDEX IND_AUDIT_DDL_SID ON   XB_AUDIT_DDL_LHR(SID,SERIAL#) LOCAL NOLOGGING;  CREATE INDEX IND_AUDIT_DDL_OBJECT_DATE ON   XB_AUDIT_DDL_LHR(OPER_DATE,OPERATION,OS_USER) LOCAL NOLOGGING ;  GRANT SELECT ON  XB_AUDIT_DDL_LHR TO PUBLIC;  创建存储过程,用于插入DDL信息到日志表中,如下所示:  CREATE OR REPLACE PROCEDURE PRO_TRI_DDL_INSET_LHR(P_SQL_FULLTEXT VARCHAR2)    AUTHID CURRENT_USER AS    SP_XB_AUDIT_DDL_LHR XB_AUDIT_DDL_LHR%ROWTYPE;    V_TMP               VARCHAR2(255);  BEGIN    SELECT A.SID,           A.SERIAL#,           (SELECT B.SPID              FROM GV$PROCESS B             WHERE B.ADDR = A.PADDR               AND B.INST_ID = USERENV('INSTANCE')) SPID,           UPPER(A.OSUSER) OSUSER,           A.MACHINE || '--' || A.PROGRAM || '--' || A.MODULE || '--' ||           A.ACTION SESSION_TYPE,           SUBSTR(P_SQL_FULLTEXT, 1, 3900),           A.SQL_ID,           A.PREV_SQL_ID,           A.USERNAME,           A.INST_ID      INTO SP_XB_AUDIT_DDL_LHR.SID,           SP_XB_AUDIT_DDL_LHR.SERIAL#,           SP_XB_AUDIT_DDL_LHR.SPID,           SP_XB_AUDIT_DDL_LHR.OS_USER,           SP_XB_AUDIT_DDL_LHR.SESSION_TYPE,           SP_XB_AUDIT_DDL_LHR.SQL_TEXT,           SP_XB_AUDIT_DDL_LHR.SQL_ID,           SP_XB_AUDIT_DDL_LHR.PREV_SQL_ID,           SP_XB_AUDIT_DDL_LHR.DB_SCHEMA,           SP_XB_AUDIT_DDL_LHR.INST_ID      FROM GV$SESSION A     WHERE A.AUDSID = USERENV('SESSIONID')       AND A.INST_ID = USERENV('INSTANCE');    INSERT INTO XB_AUDIT_DDL_LHR      (ID,       INST_ID,       OPER_DATE,       OPERATION,       OBJECT_TYPE,       OBJECT_NAME,       SQL_TEXT,       SQL_FULLTEXT,       OS_USER,       CLIENT_IP,       CLIENT_HOSTNAME,       DB_SCHEMA,       SID,       SERIAL#,       SPID,       SESSION_TYPE,       DATABASE_NAME,       OBJECT_OWNER,       ERRORS_INFOR,       SQL_ID,       PREV_SQL_ID)    VALUES      (S_XB_AUDIT_DDL_LHR.NEXTVAL,       USERENV('INSTANCE'), -- sp_xb_audit_ddl_lhr.INST_ID  ora_instance_num       SYSDATE,       ORA_SYSEVENT, --sys.sysevent       ORA_DICT_OBJ_TYPE,       ORA_DICT_OBJ_NAME,       SP_XB_AUDIT_DDL_LHR.SQL_TEXT,       P_SQL_FULLTEXT,       UPPER(SYS_CONTEXT('USERENV', 'OS_USER')), -- sp_xb_audit_ddl_lhr.os_user       SYS_CONTEXT('userenv', 'ip_address'), --ora_client_ip_address       SYS_CONTEXT('userenv', 'terminal'), --sys_context('userenv', 'host')       NVL2(ORA_LOGIN_USER,            SYS_CONTEXT('USERENV', 'SESSION_USER'),            SP_XB_AUDIT_DDL_LHR.DB_SCHEMA), -- SYS_CONTEXT('USERENV', 'SESSION_USER') sys.login_user       SP_XB_AUDIT_DDL_LHR.SID, ---- SYS_CONTEXT('USERENV', 'SID'),       SP_XB_AUDIT_DDL_LHR.SERIAL#,       SP_XB_AUDIT_DDL_LHR.SPID,       SP_XB_AUDIT_DDL_LHR.SESSION_TYPE || V_TMP,       ORA_DATABASE_NAME, --sys_context('USERENV', 'DB_NAME')       ORA_DICT_OBJ_OWNER,       DBMS_UTILITY.FORMAT_ERROR_STACK,       SP_XB_AUDIT_DDL_LHR.SQL_ID,       SP_XB_AUDIT_DDL_LHR.PREV_SQL_ID);    COMMIT;  EXCEPTION    WHEN OTHERS THEN      ROLLBACK;  END PRO_TRI_DDL_INSET_LHR;  下面的触发器将会话退出和数据库关闭的信息加入日志表XB_AUDIT_DDL_LHR中:  CREATE OR REPLACE TRIGGER TRI_AUDIT_LOGOFF_LHR    BEFORE LOGOFF OR SHUTDOWN ON DATABASE --database 、 SCHEMA    --退出、数据库关闭  DECLARE    PRAGMA AUTONOMOUS_TRANSACTION;  BEGIN    -- 调用存过    PRO_TRI_DDL_INSET_LHR('');  EXCEPTION    WHEN OTHERS THEN      ROLLBACK;  END;  此时若有会话退出的话,查询日志表XB_AUDIT_DDL_LHR可以看到如下的信息:  下面再创建登录和DDL触发器,脚本如下所示:  CREATE OR REPLACE TRIGGER TRI_AUDIT_DDL_LHR    -- AFTER DDL OR servererror OR logon OR STARTUP ON DATABASE --database 、 SCHEMA    --ddl语句、服务器报错、用户登陆、用户退出、数据库启动、数据库关闭    AFTER DDL OR SERVERERROR OR LOGON OR STARTUP ON DATABASE  /* WHEN (ora_dict_obj_name NOT IN    ('XB_AUDIT_DDL_LHR', 'PRO_TRI_DDL_INSET_LHR') OR    ora_dict_obj_name IS NULL)*/  DECLARE    PRAGMA AUTONOMOUS_TRANSACTION;    N              NUMBER;    V_SQL_FULLTEXT VARCHAR2(32767) := NULL;    SQL_TEXT       ORA_NAME_LIST_T;  BEGIN    IF ORA_SYSEVENT = 'LOGON' THEN      ----------- 填充 v$session 的 CLIENT_INFO 和  CLIENT_IDENTIFIER   列      DBMS_APPLICATION_INFO.SET_CLIENT_INFO(SYS_CONTEXT('userenv',                                                        'ip_address'));      DBMS_SESSION.SET_IDENTIFIER(SYS_CONTEXT('userenv', 'HOST'));    ELSE      --得到执行的DDL语句      BEGIN        N := ORA_SQL_TXT(SQL_TEXT);        FOR I IN 1 .. N LOOP          V_SQL_FULLTEXT := V_SQL_FULLTEXT || SQL_TEXT(I);        END LOOP;      EXCEPTION        WHEN OTHERS THEN          ROLLBACK;      END;    END IF;    --插入日志    PRO_TRI_DDL_INSET_LHR(V_SQL_FULLTEXT);  EXCEPTION    WHEN OTHERS THEN      ROLLBACK;  END TRI_AUDIT_DDL_LHR;  测试DDL触发器,创建表、TRUNCATE表、删除表,如下所示:  CREATE TABLE TEST_DDL AS SELECT * FROM DUAL;  TRUNCATE TABLE TEST_DDL;  DROP TABLE TEST_DDL;  SELECT * FROM XB_AUDIT_DDL_LHR T WHERE T.OBJECT_NAME='TEST_DDL';  若有服务器的错误,也可以被记录下来,如下所示:  SELECT * FROM XB_AUDIT_DDL_LHR T WHERE T.OPERATION='SERVERERROR';  本小节的监控内容也解决了前面提出的一个问题,“如何监控会话的登录登出情况?”,可以通过查询日志表XB_AUDIT_DDL_LHR来解决,如下所示:  SELECT T.OS_USER, T.CLIENT_IP, COUNT(1)    FROM XB_AUDIT_DDL_LHR T   GROUP BY T.OS_USER, T.CLIENT_IP;

About Me:小麦苗

● 本文作者:小麦苗,只专注于数据库的技术,更注重技术的运用

● 作者博客地址:http://blog.itpub.net/26736162/abstract/1/

● 本系列题目来源于作者的学习笔记,部分整理自网络,若有侵权或不当之处还请谅解

● 版权所有,欢迎分享本文,转载请保留出处

● 题目解答若有不当之处,还望各位朋友批评指正,共同进步