目前两套生产环境采用Oracle流复制进行数据同步,近期因为临时表空间问题导致流复制中断影响数据同步,以下是问题分析和处理过程。
一、 问题描述
前段时间,自一个生产业务用迁移到本地OSS点后,每隔几天就会在晚上21点左右,就会收到本地OSS点流复制捕获进程中断的告警短信。登陆服务器查看数据库告警日志,基本上都是由于如下临时表空间不足导致的。
根据alert告警日志显示,数据库在某一时刻报 ORA-1652: unable to extend temp segment by 128 in tablespace,提示临时表空间不足,随着时间推移,临时表空间持续被大量占用,最终导致流复制无法使用临时表空间而将捕获器进程宕。
这套数据库,是三节点RAC数据库,数据库版本是Oracle 11.2.0.4,补丁也都打了。三节点上的所有用户共用一个临时表空间,前期已为该临时表空间分配了200G以上空间,在报临时表空间不足时,为其又进行了扩容,并将其扩容到了300G,但隔了没多久又出现此情况。
二、 分析过程
查询临时表空间使用率,如下,显示。
临时表空间用来管理数据库排序操作以及用于存储临时表、中间排序结果等临时对象,当ORACLE里需要用到SORT的时候,并且当PGA中sort_area_size大小不够时,将会把数据放入临时表空间里进行排序。像数据库中一些操作: CREATE INDEX、 ANALYZE、SELECT DISTINCT、ORDER BY、GROUP BY、 UNION ALL、 INTERSECT、MINUS、SORT-MERGE JOINS、HASH JOIN等都可能会用到临时表空间。当操作完成后,系统会自动清理临时表空间中的临时对象,自动释放临时段。这里的释放只是标记为空闲、可以重用。
然后查询问题发生时段占用临时表空间的SQL如下:
可以看到该SQL单次执行时占用60多G临时表空间,如果很多应用同时执行,就会把临时表空间耗尽。
另外可以看到占用临时表空间的临时段的类型都是LOB_DATA和LOB_INDEX类型,对相关的SQL语句进行分析,相关SQL语句如下:
# SQL 已脱敏
SELECT TO_CHAR (WMSYS.WM_CONCAT (a.account_id)) FROM CRM_ACC a, OSS_ADM_U c WHERE a.owner_id = user_id AND PROPERTY = '1' AND ADD_MONTHS (SYSDATE, -24) >= A.MOD_TIME AND HOMEPAGE IS NULL AND NOT EXISTS (SELECT 1 FROM SUP_CONT_PERSON S WHERE S.ACCOUNT_ID = A.ACCOUNT_ID AND S.STATUS = '1' AND (S.TELEPHONE IS NOT NULL OR S.FAX IS NOT NULL))
复制
SELECT * FROM (SELECT row_.*, ROWNUM rownum_ FROM ( SELECT T.RESOURCE_ID, T.COM_ID, T.RES_TYPE, T.RES_STATUS, T.RES_TITLE, T.RES_NAME, T.RES_CATEGORY, T.RES_PATTERN, T.RES_VSTATUS, T.RES_VID, T.RES_WIDTH, T.RES_HEIGTH, T.RES_SIZE, T.CHE_NO, T.CHEC_NAME, T.CHE_ADVISE, T.CHECK_TIME, T.ADDER_NO, T.ADDER_NAME, T.ADDER_TIME, T.UPDATER_NO, T.UPDATER_NAME, T.UPDATER_TIME, T.RES_COVER_URL, (SELECT NVL ( TO_CHAR (WMSYS.WM_CONCAT (A.RELATION_ID)), '') FROM XXX.CORE_RES_MAP A WHERE A.RESOURCE_ID = T.RESOURCE_ID AND A.DELETE_STATUS = '0' AND A.RELATION_TYPE = '0') AS PRODUCT_IDS FROM XXX.CORE_PRO_CN_RES T WHERE T.COM_ID = :1 AND T.RESOURCE_TYPE = :2 AND T.RESOURCE_VSTATUS = '2' AND T.RESOURCE_STATUS IN (:3) ORDER BY T.ADDER_TIME DESC) row_) w
复制
查看SQL查询所使用的表,并无LOB类型字段,可为什么临时表表段类型会是LOB_DATA和LOB_INDEX呢。
通过资料查询到是因为SQL语句里使用了WMSYS.WM_CONCAT函数。WMSYS.WM_CONCAT是Oracle 10G推出的,用来连接字符串,这套库也是从Oracle 10g升级过来的。
WMSYS.WM_CONCAT在不同的数据库版本里返回类型不同,在11.2,0.4前返回是Varchar类型,而在11.2.0.4上返回LOB类型,这套生产库刚好是11.2.0.4。在11G版本,目前推荐LISTAGG函数。
Oracle在对包含lob字段进行读取、修改或写入时均使用临时表空间TEMPORARY LOB_DATA段以缓存形式来存储这些临时数据。如果是通过过应用程序来访问LOB字段时Oracle并不会显式地去关闭或清理临时LOB,
由于Oracle作为服务端并不知道客户端应用程序什么时候结束对LOB临时段的使用,所以难以对LOB临时段进行释放。
如果是Java程序,可通过java.sql.NClob.free()这个方法来释放lob,但这会牵涉到代码的改造,短期很难见到效果。
三、 解决办法
于是到Oracle 官网上搜寻有无解决办法,根据Oracle MOS ( How to Release the Temp LOB Space and Avoid Hitting ORA-1652 (Doc ID 802897.1))文档资料,建议使用
alter session set events ‘60025 trace name context forever’;
通过设置event 60025来强制会话在COMMIT之后强制释放临时表空间上的LOB。
为了测试event 60025是否会对数据库有影响,我先在即将下线的中文网通点上执行了一段时间,观察数据库无异常。于是在另一个测试点设置系统级 alter system set events ‘60025 trace name context forever’;
目前这套生产库在设置event 60025后已运行了近一周时间,目前观察数据库正常,告警日志未出现临时表空间不足的情况。
目前观察占用临时表空间的LOB类型都不太大。
另外也跟项目组人员反馈,对相关SQL进行业务改造,建议对WMSYS.WM_CONCAT函数改造,使用LISTAGG函数。