Oracle异常(一)

ORA-12537: Network Session: End of file

ORA-12537:网络会话:文件结束

在程序运行是报错,最后发现是连接数超过最大连接数

查询连接数的语句

select username,count(username) ,machine from v$session group by username,machine

官方文档:

ORA-12537 is an informational message and means that the connection has been closed. This error message can happen due to any of the following reasons:

  1. There are too many connections being open by the application.
  2. There are configuration issues in the sqlnet.ora, protocol.ora and listener.ora files.
  3. Database is shut down (maybe for nightly backup), but connection to database was kept by client.
  4. A timeout occurred on the client connection.
  5. A firewall closed idle connections.
  6. There is a path name that is too long for the Oracle TNS client on Windows.

 

应用程序打开的连接太多。
sqlnet.ora、protocol.ora和listener.ora文件中存在配置问题。
数据库已关闭(可能用于夜间备份),但与数据库的连接由客户端保留。
客户端连接超时。
防火墙关闭了空闲连接。
对于Windows上的Oracle TNS客户端来说,路径名太长。

 

因Oracle中有Inactive session,如果不处理会影响系统,但如果设置定期清理空闲会话,也会引发此异常
因为连接池缘故,应用程序关闭会话后,连接池对应的数据库会话一直处于INACTIVE状态,人为设定的自动清理作业把连接清掉,而此时程序再次访问数据时,由于连接池的相关会话被清理掉了,从而报错。--比较极端的情况,但有概率发生的

关于Oracle的Inactive session

在Oracle数据库中,"Inactive session"指的是一个已经建立连接但当前处于不活跃状态的数据库会话。这可能是因为会话已经执行完毕,但连接还没有被关闭,或者因为会话正在等待某些资源或事件的完成而暂时处于空闲状态。

这种情况通常发生在以下情况下:

1. **长时间的空闲时间**:会话已经完成了它的任务,但连接还没有被关闭。这可能是由于应用程序的设计或者错误,导致连接没有被正确地关闭。

2. **等待资源**:会话可能在执行一个需要等待某些资源的操作,比如锁定资源或等待I/O操作完成。在等待资源的过程中,会话可能会被标记为"Inactive"。

3. **网络问题**:在某些情况下,网络问题可能导致会话在不活跃的状态下停留更长时间,即使数据库中的操作已经完成。

4. **应用程序逻辑问题**:应用程序的错误逻辑可能导致会话在不活跃状态下停留,比如处理异常时没有正确关闭连接。

一般来说,对于数据库性能和资源管理来说,及时关闭不活跃的会话是很重要的。这可以通过监视会话活动并在必要时采取适当的措施来实现,比如设置超时时间、优化SQL查询、检查应用程序代码等。

如果不处理Inactive session,不活跃的会话会一直存在,占用数据库资源并且可能影响数据库的性能。特别是在高负载的情况下,大量的不活跃会话可能会导致数据库连接池用尽,影响其他用户的连接请求。此外,不活跃的会话也可能会在系统中产生不必要的锁定或资源等待,进一步影响数据库的整体性能和可用性。因此,及时处理不活跃的会话是确保数据库系统正常运行和提供稳定性服务的重要步骤。

不活跃的会话会影响数据库连接池的性能和效率。【数据库连接池】是一种管理数据库连接的工具,它可以帮助应用程序更有效地利用数据库连接资源,提高系统的性能和扩展性。然而,如果不活跃的会话占用了连接池中的连接资源,将会导致连接池中的可用连接数量减少,进而影响其他活跃会话的获取连接和执行操作的效率。因此,对于不活跃的会话需要及时关闭或释放数据库连接,以避免对数据库连接池的影响。

取消使用数据库连接池可能会减少一些与连接池相关的问题,但并不会完全消除不活跃会话导致的影响。即使不使用连接池,不活跃的会话仍然会占用数据库资源,并可能对数据库的性能产生负面影响。

当【不使用连接池】时,每个会话都会直接与数据库建立连接,因此不活跃的会话仍然会保持数据库连接,并在一段时间内占用数据库资源。这可能会导致数据库资源的浪费和性能下降,尤其是在高负载情况下。此外,不活跃的会话也可能导致数据库中的锁定或资源等待,影响其他会话的执行效率和系统的整体性能。

因此,无论是否使用连接池,及时关闭或释放不活跃的会话仍然是维护数据库性能和资源管理的重要步骤


 

在Oracle中,连接池通常会维护一组数据库会话,以便在应用程序需要时可以快速获取数据库连接。当应用程序关闭会话后,连接池中对应的数据库会话可能会进入INACTIVE状态。INACTIVE状态表示该会话当前没有执行任何活动的SQL语句,但连接仍然保持着以供将来使用。

连接池中的会话处于INACTIVE状态并不意味着它们被完全关闭或终止,而是暂时处于空闲状态以等待将来的数据库连接请求。这有助于减少每次请求时重新建立数据库连接的开销,提高系统性能和资源利用率。

需要注意的是,虽然连接池中的会话处于INACTIVE状态,但它们仍然占用一定的数据库资源,包括连接资源和内存资源。因此,在设计应用程序和配置连接池时,需要考虑适当的连接管理策略,以确保数据库资源得到有效利用并且不会被浪费。

通常情况下,连接池会优先重用处于INACTIVE状态的连接,而不是创建新的连接。这是因为重用已存在的连接可以减少数据库系统的开销和资源消耗,提高系统性能和响应速度。当连接池中存在INACTIVE状态的连接时,应用程序请求连接时,连接池会尝试重新激活这些空闲连接,以满足请求,而不是每次都创建新的连接。

然而,是否重用INACTIVE状态的连接还取决于连接池的配置和管理策略。一些连接池可能会设置最大空闲时间,超过该时间后会关闭INACTIVE状态的连接,而不是重用它们。此外,连接池还可能会根据当前的连接负载和性能需求来动态调整连接的创建和重用策略。

总的来说,连接池通常会尽量重用处于INACTIVE状态的连接,但具体的行为可能会根据连接池的配置和应用程序的需求而有所不同。


 

Oracle的连接池通常不会自动清理处于INACTIVE状态的连接,而是保留这些连接以待重新激活和重用。但是,一些连接池实现可能会提供连接清理和回收机制,以确保连接池中的连接不会过度,以确保连接池中的连接不会过度累积或占用过多资源。这些连接池通常会根据一定的策略或配置参数来判断何时清理处于INACTIVE状态的连接,例如根据连接的空闲时间、连接的最大存活时间、连接数量等条件。常见的一些连接池,如Apache Commons DBCP、HikariCP、C3P0等,可能提供连接清理机制以优化连接的管理和资源利用。具体的清理策略和行为可能因连接池实现而异,因此在使用连接池时需要了解各个连接池的特性和配置选项。


在Oracle数据库中,使用连接池的会话正常结束后,会话状态通常会变为INACTIVE,而不是立即被清理。当会话被标记为INACTIVE后,连接池会保留这些会话以备重用,避免频繁地创建和关闭连接带来的开销。

如果不使用连接池,在Oracle中正常结束的会话也会被关闭,而不会保留为INACTIVE状态。Oracle数据库会自动管理和释放这些会话,确保数据库资源的正确释放和维护。

总的来说,使用连接池会更好地管理和优化数据库连接的使用,但如果不使用连接池,Oracle数据库也会自动处理正常结束的会话,不会保留为INACTIVE状态。

查询Inactive session:

SELECT SID, SERIAL#, STATUS, USERNAME, OSUSER, MACHINE, PROGRAM
FROM V$SESSION
WHERE STATUS = 'INACTIVE';

在Oracle数据库中,如果不处理不活跃会话可能会对系统资源和服务器内存造成一些影响:

1. 资源浪费:不活跃的会话会持续占用数据库资源,包括连接资源、内存资源和服务器处理资源。长时间保持不活跃的会话可能会导致资源的浪费,降低数据库的性能和可用性。

2. 内存占用:不活跃的会话在内存中占用存储空间,会增加总体内存占用。如果有大量不活跃的会话积累,可能会导致内存压力增加,甚至引起内存资源不足的情况。

3. 系统性能下降:不活跃的会话会竞争数据库资源,可能会导致查询响应时间延长、系统负载增加、数据库性能下降等问题。长时间存在大量不活跃的会话会对整个系统的性能产生负面影响。

因此,及时识别和处理不活跃会话是维护Oracle数据库系统性能和资源管理的重要步骤,可以通过定期监控、设置超时机制、合理调整连接参数等方式来有效管理不活跃会话,减少对系统资源和服务器内存的影响。

在Oracle数据库中,Inactive session通常会在以下情况下被重新激活

1. 用户执行SQL查询或操作:当不活跃的会话接收到用户发起的SQL查询或操作时,会话将会重新激活以执行相应的任务。

2. 定时任务或后台作业:如果不活跃的会话有定时任务或后台作业需要执行,会话会被重新激活以完成相应的任务。

3. 会话超时:如果设置了数据库连接的超时机制,超过一定时间没有活动的不活跃会话可能会被数据库系统自动终止,而重新激活会话可能需要用户再次连接数据库。

4. 用户手动重新连接:用户可能会手动重新连接到先前不活跃的会话,这将会重新激活该会话。

需要注意的是,具体的Inactive session何时会被重新激活可能会受到数据库配置、应用程序设计和用户操作的影响。为了有效管理和优化数据库性能,建议适时监控和处理不活跃会话,并根据实际情况制定相应的重新激活策略


在Oracle中,如何定时清理INACTIVE状态的会话?

讨论:

状态
1.活跃会话基本上无法处理,Oracle数据库拆分[极端情况例如:会话挤压等异常情况,处理数据库异常后,活跃会话趋于平稳】;
2.非活跃会话可以定期清理

参考文档
https://www.anbob.com/archives/5837.html


非活跃会话方法论:
A:dead session
Dead connection detection(DCD)是Oracle网络功能,主要用于客户端关闭其系统电源或客户端计算机意外崩溃而又无法正常关闭Oracle数据库连接的环境。
如果客户端计算机因正确断开会话而突然崩溃,则这些会话锁定的资源将继续被锁定,从而导致环境性能问题。由于这种情况可以在任何环境中发生,因此Oracle网络服务提供了DCD的功能,可以在早期检测到这种情况,从而可以快速恢复锁定的资源。
为了解决这种情况并检测死连接,Oracle在网络会话(12c中的tcp)层引入了一个新概念。服务器进程将SQL * Net Probe数据包发送到客户端,以检查在sqlnet.expire_time【单位:分钟】参数指定的每个固定时间间隔内连接是否仍然可用。如果通过探测数据包的通信失败,则会返回错误,导致服务器进程退出。
SQLNET.EXPIRE_TIME = 10  

B:idel session
1.WAS等中间件或程序软件,存在会话一定时间不工作自动断开设置;
2.MAX_IDLE_TIME指定会话可以空闲的最大分钟数。 此后会话将自动终止。12.2 引入的新参数。这参数是整个db或CDB级,不可以alter session.  单位分钟,超过idle上限时间后会收到 ORA-03113错误。
3.编写plsql脚本,定期执行[如下详细说明]

C:idle blocker session
1.目前常规基本上都是DBA人为参与处理;
2.如果一个idle session阻塞了其他会话,在19c 20c中可以使用参数max_idle_blocker_time 自动终止blocker, 当会话持有其他会话所需的资源时,该会话被视为阻塞会话.
   1)如 该会话持有另一个会话所需的锁。
   2)该会话是并行操作,并且其使用者组,PDB或数据库已达到其最大并行服务器限制或已排队的并行操作。
   3)会话的PDB或数据库实例即将达到其SESSIONS或PROCESSES限制。
此参数与MAX_IDLE_TIME参数的不同之处在于,MAX_IDLE_TIME适用于所有会话(阻塞和非阻塞),而MAX_IDLE_BLOCKING_TIME仅适用于阻塞会话。  因此,为了使MAX_IDLE_BLOCKING_TIME有效,其限制必须小于MAX_IDLE_TIME限制。

 [活跃说明数据库存在大量并发,正常情况下说明是业务负载这块只能拆库,常规无法优化套路】

1) 内存消耗,每个会话对应数据库来说都是一个pga process 需要单独分配一块内存区域,并且其中会话保留的游标越多,越消耗内存;

2) cpu资源消耗,每个会话对应操作系统来说都是一个Oracle用户进程,虽然大量属于inactive 但是对于操作系统来说还是一个进程运行

可以考虑以下策略:

  1. 增加会话超时时间:修改数据库参数SQLNET.EXPIRE_TIME,该参数定义了服务器端会话在被自动终止前可以保持空闲的分钟数。
  2. 定期执行数据库清理脚本:可以编写一个脚本定期检查并关闭那些已空闲特定时间的会话。
  3. 使用会话池管理:在应用程序层面管理数据库会话,以减少不必要的空闲会话。
  4. 监控和管理用户权限:限制用户权限,避免用户无意中打开不必要的会话。

示例脚本(Oracle PL/SQL),用于关闭空闲超过特定时间的会话:

DECLARE
  v_idle_time NUMBER := 60; -- 设置空闲时间为60分钟
BEGIN
  FOR s IN (SELECT sid, serial#, username FROM v$session WHERE username IS NOT NULL AND username != 'SYS' AND STATUS = 'INACTIVE' AND logon_time < SYSDATE - (v_idle_time / 24 / 60) )
  LOOP
    EXECUTE IMMEDIATE 'ALTER SYSTEM KILL SESSION ''' || s.sid || ',' || s.serial# || ''' IMMEDIATE';
  END LOOP;
END;
/

 


一般情况下,少量的INACTVIE会话对数据库并没有什么影响,但是,如果由于程序设计等某些原因导致数据库出现大量的会话长时间处于INACTIVE状态,那么将会导致大量的系统资源被消耗,造成会话数超过系统SESSION的最大值,出现ORA-00018:maximum number of sessions exceeded错误。此时就需要清理那些长时间处于INACTIVE状态的会话。人为定期检查、杀掉这类会话肯定不太现实,要定期清理那些长时间处于INACTIVE的会话,可以使用如下几种办法:
1. sqlnet.ora文件里加上sqlnet.expire_time,单位为分钟数。
2. 设置用户profile的IDLE_TIME参数,需要设置resource_limit为true,然后再设置IDLE_TIME参数,单位为分钟:
alter system set resource_limit=true;
alter profile default limit idle_time 10;
方法2需要和方法1结合使用。
3. 直接KILL掉INACTIVE的会话。V$SESSION视图中的LAST_CALL_ET字段表示用户最后一条语句执行完毕后到sysdate的时间,单位为秒。每次用户执行一个新的语句后,该字段复位为0,重新开始记数。可以通过该字段来获得一个连接用户最后一次操作数据库后的空闲时间。推荐使用这种方法来释放INACTIVE状态的会话。具体代码如下所示:

set sqlblanklines on
CREATE OR REPLACE PROCEDURE P_kill_session_LHR AS
        ----------------------------------------------------------------------------------
        -- function:  杀掉10个小时之前的会话 ,告警日志中会记录被杀掉的会话信息
        ----------------------------------------------------------------------------------
    BEGIN
        -- IF to_char(SYSDATE, 'HH24') >= '20' OR
        --     TO_CHAR(SYSDATE, 'HH24') <= '08' THEN
        FOR cur IN (SELECT A.USERNAME,
                           A.LOGON_TIME,
                           A.STATUS,
                           A.SID,
                           A.SERIAL#,
                           A.MACHINE,
                           A.OSUSER,
                       'ALTER SYSTEM  DISCONNECT SESSION ''' || a.SID || ',' ||
                       a.serial# || ',@' || a.INST_ID || ''' IMMEDIATE' kill_session
                    FROM   gv$session A
                    WHERE  A.STATUS IN ('INACTIVE')
                    AND    A.USERNAME IS NOT NULL
                    AND    A.LAST_CALL_ET >= 60 * 60 * 10) LOOP
            BEGIN
                EXECUTE IMMEDIATE cur.kill_session;
            EXCEPTION
                WHEN OTHERS THEN
                    NULL;
            END;
        END LOOP;
        -- END IF;
    EXCEPTION
        WHEN OTHERS THEN
            NULL;
    END P_kill_session_LHR;
/
BEGIN
    --DBMS_SCHEDULER.drop_job('JOB_P_kill_session_LHR');
    DBMS_SCHEDULER.CREATE_JOB(JOB_NAME        => 'JOB_P_kill_session_LHR',
                              JOB_TYPE        => 'STORED_PROCEDURE',
                              JOB_ACTION      => 'P_kill_session_LHR',
                              repeat_interval => 'FREQ=MINUTELY;INTERVAL=60',
                              ENABLED         => TRUE,
                              START_DATE      => SYSDATE,
                              COMMENTS        => '删除--每60分钟检查一次');
END;
/

 

检查语句:

-- Check inactive and active session count
select status, count(1) from v$session group by status;
--Check username,programname inactive count
select username, program, count(1) from v$session where status='INACTIVE' group by username, program;
--Find more details of inactive count
select p.username "OSUSERNAME", p.terminal,p.program,s.username "DBUSERNAME",s.command,s.status,s.server,s.process,s.machine,s.port,s.terminal,s.program,s.sid,s.serial#,p.spid FROM v$session s,v$process pWHERE p.addr=s.paddr and s.status='INACTIVE'order by 1,4;

Inactive session是由于Dead Connection或IDLE Connection造成的

DEAD 连接由 SQLNET.ORA 文件处理,通过配置参数 SQLNET.EXPIRE_TIME=minutes 打开 SQLNET ORA 文件并设置参数。它是客户端文件,所以在客户端设置它。

SQLNET.EXPIRE_TIME=60 (1 hour)

IDLE 连接设置为 USER PROFILES,定义具有 IDLE_TIME 限制的配置文件,以便在达到时间限制后终止 INACTIVE SESSION。

-- Need to enable resource limit so it automatic terminate the session.alter system set resource_limit=true scope=both;
--Check user which profile it uses
SELECT USERNAME, USER_ID, PROFILE FROM DBA_USERS;
--check profile setting for IDLE_TIME
select profile, limit from DBA_PROFILES where resource_name = 'IDLE_TIME';
--Alter IDLE TIME to 30 minutes after it disconnect
alter profile customers_profiles limit idle_time 30;

注意:idle_time 参数将在 n 分钟不活动后断开会话。

 另外需要注意,Kill掉这些会话需要需要谨慎,稍不注意,就有可能误杀了一些正常的会话。那么我们该如何定义这类会话呢?下面是我结合业务规则定义的:
  1: 会话的Status必须为INACTIVE,如果会话状态为ACTIVE、KILLED、CACHED、SNIPED状态,不做考虑。
  2: 会话必须已经长时间处于INACTIVE状态。例如,处于INACTIVE状态超过了两小时的会话进程,才考虑Kill。这个视具体业务或需求决定,有可能超过半小时就可以杀掉会话进程。至于如何计算处于INACTIVE会话状态的时间,这个可以 通过V$SESSION的LAST_CALL_ET字段来判别,需要查询处于INACTIVE状态两小时或以上的会话,就可以通过查询条件S.LAST_CALL_ET >= 60*60*2实现,当然最好写成 S.LAST_CALL_ET >= 7200
  3: 连接到会话的程序。比如,某个特定的应用程序产生的INACTIVE会话才要清理。例如, Toad工具、PL/SQL Developer工具。关于PROGRAM这个需要根据当前项目的具体情况设置,下面仅仅使用TOAD.EXE、W3WP.EXE举例说明。

SELECT SID, SERIAL#,MODULE, STATUS
FROM V$SESSION S
 WHERE S.USERNAME IS NOT NULL
AND UPPER(S.PROGRAM) IN ('TOAD.EXE', 'W3WP.EXE')
AND S.LAST_CALL_ET >= 60*60*2
AND S.STATUS = 'INACTIVE'
  ORDER BY SID DESC;

如果是RAC环境,那么最好使用下面SQL语句,使用全局视图GV$SESSION。

 SELECT SID, SERIAL#, INST_ID, MODULE,STATUS
FROM gv$session S
WHERE S.USERNAME IS NOT NULL
AND UPPER(S.PROGRAM) IN ('TOAD.EXE', 'W3WP.EXE')
 AND S.LAST_CALL_ET >= 2 * 60*60
AND S.STATUS = 'INACTIVE'
 ORDER BY INST_ID DESC

单实例查询

select sid, serial#, module, status
 
  from v$session s
 
 where s.username is not null
 
   and upper(s.program) in ('TOAD.EXE', 'W3WP.EXE', 'PLSQLDEV.EXE')
 
   and s.last_call_et >= 60
 
   and s.status = 'INACTIVE'
 
 order by sid desc;

集群查询:

select sid, serial#, inst_id, module, status
 
  from gv$session s
 
 where s.username is not null
 
   and upper(s.program) in ('TOAD.EXE', 'W3WP.EXE', 'PLSQLDEV.EXE')
 
   and s.last_call_et >= 60
 
   and s.status = 'INACTIVE'
 
 order by inst_id desc;

 

接下来创建存储过程SYS.DB_KILL_IDLE_CLIENTS. 方便调用该功能执行kill inactive 会话。注意:xxx部分用实际业务的PROGRAM来替代。

 CREATE OR REPLACE PROCEDURE SYS.DB_KILL_IDLE_CLIENTS AUTHID DEFINER AS
   job_no number;
   num_of_kills number := 0;
 BEGIN
  
    FOR REC IN
        (SELECT SID, SERIAL#, INST_ID, MODULE,STATUS
        FROM gv$session S
            WHERE S.USERNAME IS NOT NULL
            AND UPPER(S.PROGRAM) IN ('xxx', 'xxx.EXE')   
                 AND S.LAST_CALL_ET >= 2*60*60                              
             AND S.STATUS= 'INACTIVE'
         ORDER BY INST_ID ASC
             ) LOOP
          ---------------------------------------------------------------------------
         -- kill inactive sessions immediately
         ---------------------------------------------------------------------------
         DBMS_OUTPUT.PUT('LOCAL SID ' || rec.sid || '(' || rec.module || ')');
     execute immediate 'alter system kill session ''' || rec.sid || ', ' ||
                            rec.serial# || '''immediate' ;
      
         DBMS_OUTPUT.PUT_LINE('. killed locally ' || job_no);
         num_of_kills := num_of_kills + 1;
  END LOOP;
  DBMS_OUTPUT.PUT_LINE ('Number of killed xxxx system sessions: ' || num_of_kills);
 END DB_KILL_IDLE_CLIENTS;
 

另外,由于kill session是直接将session kill掉,有可能出现导致事物回滚的现象,其实我们可以使用disconnect session完成当前事务并终止session。这种方式比alter system kill session跟安全可靠。

 CREATE OR REPLACE PROCEDURE SYS.DB_KILL_IDLE_CLIENTS AUTHID DEFINER AS
       job_no number;
       num_of_kills number := 0;
    BEGIN
     
       FOR REC IN
           (SELECT SID, SERIAL#, INST_ID, MODULE,STATUS
            FROM gv$session S
                WHERE S.USERNAME IS NOT NULL
               AND UPPER(S.PROGRAM) IN ('xxxx', 'xxxx')   
                    AND S.LAST_CALL_ET >= 2*60*60                              
               AND S.STATUS<>'KILLED'
           ORDER BY INST_ID ASC
               ) LOOP
            ---------------------------------------------------------------------------
            -- kill inactive sessions immediately
            ---------------------------------------------------------------------------
            DBMS_OUTPUT.PUT('LOCAL SID ' || rec.sid || '(' || rec.module || ')');
            execute immediate 'alter system disconnect session ''' || rec.sid || ', ' ||
                               rec.serial# || '''immediate' ;
    
            DBMS_OUTPUT.PUT_LINE('. killed locally ' || job_no);
            num_of_kills := num_of_kills + 1;
      END LOOP;
      DBMS_OUTPUT.PUT_LINE ('Number of killed system sessions: ' || num_of_kills);
   END DB_KILL_IDLE_CLIENTS;
   

然后,我们可以在作业(JOB)或Schedule里面定期调用该存储过程,也可以通过后台作业结合shell脚本实现定期清理空闲会话的功能。例如如下所示。

创建killSession.sh脚本,调用该存储过程SYS.DB_KILL_IDLE_CLIENTS

 #!/bin/bash
 logfile=/home/oracle/cron/session/log/killSession.log
 echo " " >> $logfile 2>&1
 echo "START ----`date`" >> $logfile 2>&1
 sqlplus /nolog <<STATS
 connect / as sysdba
 exec sys.db_kill_idle_clients;
 exit;
 STATS
 echo "END ------`date`" >> $logfile 2>&1

在crontab里面配置后台作业,每隔15分钟运行一次,清理哪些满足条件的空闲会话。

0,15,30,45 * * * * /home/oracle/cron/session/bin/killSession.sh >/dev/null 2>&1

 

posted @ 2024-05-11 10:12  IT苦行僧-QF  阅读(22)  评论(0编辑  收藏  举报