资讯动态

Oracle历史锁追溯:AWR与ASH实战指南

发布时间:2026/9/19 0:28:40 来源:尧图企业网站定制
1. 为什么“查历史锁”这件事90%的DBA都搞错了方向在Oracle数据库运维现场我见过太多次这样的场景业务突然卡住应用日志里满屏ORA-00054resource busy and acquire with NOWAIT specified开发同事火急火燎地冲过来问“谁锁了这张表”——这时候老手会立刻登录数据库敲出SELECT * FROM V$LOCKED_OBJECT、SELECT * FROM V$SESSION WHERE BLOCKING_SESSION IS NOT NULL三下五除二定位到那个正在执行UPDATE却忘了COMMIT的会话杀掉就完事。整个过程行云流水五分钟解决战斗。但问题来了如果这个锁在两分钟前就已经释放了呢如果故障发生在凌晨三点而你六点才被叫醒如果开发坚称“我昨天下午五点改完数据就下班了不可能还锁着”可业务方提供的报错时间戳确凿无疑这时候翻遍V$系列动态性能视图你只会看到一片空荡荡的no rows selected。因为所有V$开头的视图本质都是内存快照——它们只反映数据库实例当前这一毫秒的运行状态。锁一旦释放相关记录瞬间蒸发就像从未存在过。这就是“Oracle查看历史锁信息”这个标题背后最根本的认知陷阱很多人以为这是个简单的SQL查询题其实它是个数据持久化行为审计时间回溯的复合工程。你真正要找的不是“现在谁在锁”而是“过去某一时刻谁锁过什么、锁了多久、为什么没释放”。这已经超出了V$视图的能力边界必须转向AWRAutomatic Workload Repository和ASHActive Session History这两套Oracle内置的“黑匣子”系统。它们像飞机上的飞行记录仪不实时干预运行但会以固定频率默认每小时一次AWR快照每秒采样一次ASH把数据库的“生命体征”持续写入磁盘。历史锁事件就藏在这些被持久化的快照数据里。关键词里反复出现的“oracle 11g 下载资源”“oracle 19c”“oracle安装教程”看似无关实则暗含线索不同版本对历史锁信息的采集粒度和保留策略差异巨大。Oracle 11g默认只保留8天的ASH数据而19c在合理配置下可支撑30天以上11g的AWR快照默认每小时一次若锁事件恰好发生在两次快照之间就可能被“漏采”。所以当你听到“查不到历史锁”第一反应不该是SQL写错了而应立刻检查你的数据库版本是多少AWR保留期设为几天ASH采样频率是否被调低过这些底层配置才是决定你能否“看见过去”的真正开关。提示不要试图用DBA_HIST_ACTIVE_SESS_HISTORY或DBA_HIST_LOCK_ACTIVITY这类字面带“HIST”的视图直接查询。它们是AWR快照的汇总表锁信息已被高度聚合丢失了会话级细节。真正的原始“录像带”藏在DBA_HIST_ACTIVE_SESS_HISTORY的每一行记录中——每一行代表一个会话在某一秒内的活动状态其中EVENT字段值为enq: TX - row lock contention或enq: TM - contention时就是锁事件发生的铁证。2. AWR与ASHOracle数据库的两套“行车记录仪”要真正理解如何追溯历史锁必须先掰开揉碎AWR和ASH这两套机制。它们不是并列关系而是“粗粒度录像”与“高清慢动作”的互补组合。打个比方AWR就像高速公路的收费站监控每小时拍一张车流总览图告诉你某时段哪条车道拥堵严重ASH则是每辆车自带的行车记录仪以秒级精度记录方向盘角度、刹车力度、GPS坐标——虽然存储成本高但能还原任何一辆车在任意一秒的精确操作。2.1 AWR按小时刻度的宏观趋势库AWR的核心是DBA_HIST_SNAPSHOT表它记录了每次快照的起止时间、快照IDSNAP_ID、数据库IDDBID等元数据。一次标准的AWR快照会捕获数百个性能指标其中与锁直接相关的是DBA_HIST_LOCK_ACTIVITY视图。但这里有个关键认知DBA_HIST_LOCK_ACTIVITY并非原始数据源而是Oracle对DBA_HIST_ACTIVE_SESS_HISTORY中锁事件的统计聚合。它只告诉你“在SNAP_ID12345这个快照周期内共发生TX锁争用17次平均等待时长230毫秒”却无法告诉你第17次争用具体是哪个会话、锁了哪张表、SQL文本是什么。因此AWR的价值在于快速定位问题时间段。假设业务方反馈“昨天下午14:25分开始响应变慢”你可以先用以下SQL锁定可疑快照SELECT SNAP_ID, BEGIN_INTERVAL_TIME, END_INTERVAL_TIME FROM DBA_HIST_SNAPSHOT WHERE BEGIN_INTERVAL_TIME TO_DATE(2024-06-15 14:00, YYYY-MM-DD HH24:MI) AND END_INTERVAL_TIME TO_DATE(2024-06-15 15:00, YYYY-MM-DD HH24:MI) ORDER BY BEGIN_INTERVAL_TIME;结果可能返回SNAP_ID1234514:00-15:00和SNAP_ID1234615:00-16:00。接着查询这两个快照的锁活动峰值SELECT SNAP_ID, SUM(CASE WHEN EVENT_NAME enq: TX - row lock contention THEN 1 ELSE 0 END) AS TX_LOCK_COUNT, SUM(CASE WHEN EVENT_NAME enq: TM - contention THEN 1 ELSE 0 END) AS TM_LOCK_COUNT, MAX(AVERAGE_WAIT_TIME) AS MAX_AVG_WAIT_MS FROM DBA_HIST_LOCK_ACTIVITY WHERE SNAP_ID IN (12345, 12346) GROUP BY SNAP_ID;如果SNAP_ID12345的TX_LOCK_COUNT高达200次而SNAP_ID12346只有5次基本可以断定问题集中在14:00-15:00区间。这时AWR的任务就完成了——它把大海捞针的范围从“过去一周”精准压缩到“60分钟”。2.2 ASH秒级精度的会话行为录像带当AWR圈定时间范围后真正的“破案”工作才开始。DBA_HIST_ACTIVE_SESS_HISTORY简称ASH历史表就是那盘高清录像带。它的设计哲学是宁可多存不可少录。每秒对所有处于非空闲状态STATEWAITING或STATEON CPU的会话进行一次采样记录SESSION_ID、SQL_ID、EVENT、P1TEXT/P1/P2TEXT/P2锁资源标识符、CURRENT_OBJ#当前对象号等数十个字段。最关键的是SAMPLE_TIME字段精确到微秒让你能回溯到任意一秒。但ASH数据量极大直接全表扫描效率极低。必须用时间事件双重过滤。继续上面的例子若已知问题在14:00-15:00且锁类型为TX行锁则核心查询如下SELECT SAMPLE_TIME, SESSION_ID, SESSION_SERIAL#, SQL_ID, EVENT, P1TEXT, P1, P2TEXT, P2, P3TEXT, P3, CURRENT_OBJ#, OBJECT_NAME, OWNER FROM DBA_HIST_ACTIVE_SESS_HISTORY a LEFT JOIN DBA_OBJECTS o ON a.CURRENT_OBJ# o.OBJECT_ID WHERE a.SAMPLE_TIME TIMESTAMP 2024-06-15 14:00:00 AND a.SAMPLE_TIME TIMESTAMP 2024-06-15 15:00:00 AND a.EVENT enq: TX - row lock contention AND o.OWNER IS NOT NULL ORDER BY SAMPLE_TIME DESC;这段SQL的威力在于它能列出该小时内每一次TX锁等待事件的完整上下文。P1和P2是解码锁资源的关键——对于TX锁P1是锁模式如6exclusiveP2是事务的唯一标识XIDUSN.XIDSLOT.XIDSQN。通过XIDUSN回滚段号和XIDSLOT槽位号甚至能反向查出是哪个回滚段里的哪条事务在作祟。注意DBA_HIST_ACTIVE_SESS_HISTORY中的OBJECT_NAME可能为空因为采样时会话可能正等待锁尚未执行到访问对象的步骤。此时需结合SQL_ID去DBA_HIST_SQLTEXT表中提取SQL文本再分析其涉及的表名。这是实际排障中极易忽略的环节。3. 从“锁事件”到“肇事者”三步还原完整因果链查到一堆enq: TX - row lock contention记录只是起点真正的挑战是如何把零散的秒级采样拼成一条清晰的因果链谁会话→ 在什么时间精确到秒→ 执行什么SQL语句→ 锁住了哪张表的哪些行资源→ 为什么没释放阻塞源头这需要三步递进式分析缺一不可。3.1 第一步锁定“嫌疑会话”及其活跃窗口ASH记录是离散的但真实会话的生命周期是连续的。一个典型的锁阻塞场景中会存在两个关键会话持有锁的会话Holder和等待锁的会话Waiter。Waiter的ASH记录会密集出现在EVENTenq: TX - row lock contention而Holder的记录则可能显示EVENTSQL*Net message from client客户端未发COMMIT或EVENTrdbms ipc message后台进程空闲。因此第一步是找出Waiter的“活跃窗口”。观察上一步查询结果你会发现同一SESSION_ID在短时间内如10秒内连续出现多条TX锁等待记录。例如SAMPLE_TIMESESSION_IDEVENTP1P214:22:35.123456123enq: TX - row lock contention612345614:22:36.123456123enq: TX - row lock contention612345614:22:37.123456123enq: TX - row lock contention6123456这表明会话123从14:22:35开始持续至少3秒处于锁等待状态。那么它的“活跃窗口”就是14:22:35到14:22:37。接下来我们要在这个窗口内搜索所有与P2123456相关的其他会话记录——尤其是那些EVENT不为TX锁等待的会话它们极大概率就是Holder。3.2 第二步追踪“锁资源”定位Holder会话利用上一步得到的锁资源标识P2123456在相同时间窗口内搜索所有会话SELECT SAMPLE_TIME, SESSION_ID, SESSION_SERIAL#, EVENT, SQL_ID, CURRENT_OBJ# FROM DBA_HIST_ACTIVE_SESS_HISTORY WHERE SAMPLE_TIME TIMESTAMP 2024-06-15 14:22:35 AND SAMPLE_TIME TIMESTAMP 2024-06-15 14:22:38 AND P2 123456 AND SESSION_ID ! 123 -- 排除Waiter自身 ORDER BY SAMPLE_TIME;结果可能返回SAMPLE_TIMESESSION_IDEVENTSQL_IDCURRENT_OBJ#14:22:35.789012456SQL*Net message from clientabc123def789012这说明会话456在14:22:35.789时正处在网络等待状态且其持有的锁资源P2123456正是会话123所等待的。至此Holder会话锁定为456。下一步就是还原会话456到底在做什么。3.3 第三步还原Holder的完整操作轨迹会话456的EVENTSQL*Net message from client意味着它已执行完SQL正在等待客户端发送下一条命令很可能是COMMIT或ROLLBACK。要确认这一点需回溯它在锁发生前的活动-- 查询会话456在14:22:35前1分钟内的所有活动 SELECT SAMPLE_TIME, EVENT, SQL_ID, SQL_OPCODE, CURRENT_OBJ#, OBJECT_NAME FROM DBA_HIST_ACTIVE_SESS_HISTORY a LEFT JOIN DBA_OBJECTS o ON a.CURRENT_OBJ# o.OBJECT_ID WHERE SESSION_ID 456 AND SAMPLE_TIME TIMESTAMP 2024-06-15 14:21:35 AND SAMPLE_TIME TIMESTAMP 2024-06-15 14:22:35 ORDER BY SAMPLE_TIME;典型结果会显示14:21:40.123:EVENTenq: TX - row lock contention它自己也曾等待过锁说明上游有更早的Holder14:21:45.456:EVENTdb file sequential read读取数据块14:21:48.789:EVENTCPU Wait for CPU执行UPDATE逻辑14:21:50.012:EVENTSQL*Net message from clientSQL执行完毕等待客户端再通过SQL_IDabc123def关联DBA_HIST_SQLTEXT就能看到那条罪魁祸首的UPDATE语句。最终因果链成型会话456在14:21:48执行UPDATE锁定某行 → 14:21:50等待客户端指令 → 14:22:35时会话123尝试UPDATE同一行被阻塞 → 持续等待至14:22:37。实操心得我曾在一个金融系统中遇到类似案例Holder会话的SQL_ID指向一个存储过程但ASH中CURRENT_OBJ#为空。后来发现该存储过程内部动态拼接了表名而CURRENT_OBJ#只在硬解析阶段有效。解决方案是用SQL_ID查DBA_HIST_SQL_PLAN找到OPERATIONUPDATE的执行计划其OBJECT_OWNER和OBJECT_NAME字段才真正揭示了被锁的表。这是ASH分析中一个隐蔽但高频的坑。4. 配置与权限让历史锁查询从“可能”变成“稳定可靠”再精妙的查询逻辑若缺乏底层配置支撑也终将归于徒劳。Oracle的历史锁追溯能力高度依赖三个关键配置项AWR保留期、ASH采样频率、以及用户权限。这三者如同三角支架缺一不可。4.1 AWR保留期决定你能回溯多远的“时间深度”AWR快照默认保留8天11g或30天19c但这只是出厂设置。生产环境必须根据业务SLA主动调整。计算公式很简单所需保留天数 故障平均响应时间小时/ 24 安全冗余天数。例如若团队要求4小时内响应且希望留出3天分析缓冲则最小保留期为(4/24)3 ≈ 3.17天向上取整为4天。但实际中我们一律设为30天——因为AWR快照本身占用空间极小通常1GB/天而30天的覆盖范围足以应对绝大多数偶发性问题。修改命令如下需SYSDBA权限-- 查看当前设置 SELECT RETENTION FROM DBA_HIST_WR_CONTROL; -- 修改为30天43200分钟 EXEC DBMS_WORKLOAD_REPOSITORY.MODIFY_SNAPSHOT_SETTINGS(retention 43200);提示DBA_HIST_WR_CONTROL视图中的TOPNSQL参数控制ASH中Top SQL的采集数量默认为100。若系统SQL种类繁多建议调高至500避免关键SQL被挤出ASH。4.2 ASH采样频率决定你能看清多细的“动作精度”ASH默认每秒采样一次这是平衡性能与诊断精度的最佳实践。但某些极端场景如毫秒级瞬时锁可能需要更高频。Oracle允许将采样间隔缩短至100毫秒0.1秒代价是ASH数据量增加10倍对I/O和存储造成压力。是否启用取决于你的硬件预算和故障特征。启用方法需SYSDBA-- 查看当前采样间隔单位百分之一秒 SELECT VALUE FROM V$PARAMETER WHERE NAME statistics_level; -- 必须先确保statistics_levelALL默认即为此值 -- 然后修改ASH采样间隔为100毫秒 ALTER SYSTEM SET _ash_sample_interval100 SCOPESPFILE; -- 重启数据库生效注意_ash_sample_interval是隐藏参数生产环境启用前务必在测试库充分验证I/O负载。4.3 用户权限授予查询历史数据的“通行证”普通DBA账号通常只有SELECT_CATALOG_ROLE但这仅够查询V$视图。要访问DBA_HIST_*系列历史表必须显式授权-- 授予查询AWR和ASH历史表的权限 GRANT SELECT ON DBA_HIST_ACTIVE_SESS_HISTORY TO your_dba_role; GRANT SELECT ON DBA_HIST_SNAPSHOT TO your_dba_role; GRANT SELECT ON DBA_HIST_SQLTEXT TO your_dba_role; GRANT SELECT ON DBA_HIST_SQL_PLAN TO your_dba_role; GRANT SELECT ON DBA_OBJECTS TO your_dba_role; -- 若需生成AWR报告还需执行 $ORACLE_HOME/rdbms/admin/catrep.sql -- 编译报告包首次需运行一个常见疏漏是DBA_HIST_ACTIVE_SESS_HISTORY依赖DBA_OBJECTS的OWNER字段做表名关联但SELECT_CATALOG_ROLE不包含DBA_OBJECTS的SELECT权限。若忘记授权查询会因ORA-00942: table or view does not exist失败而错误提示完全不指向权限问题极易误导排查方向。5. 超越SQL用AWR报告与ADDM诊断锁定根因当ASH查询确认了锁事件的存在下一步往往是“为什么会出现这种锁”。单纯知道“谁锁了谁”不够必须回答“为什么这个会话会长时间持有锁”。这时单条SQL已力不从心需借助Oracle的自动化诊断工具——AWR报告与ADDMAutomatic Database Diagnostic Monitor。5.1 AWR报告一份结构化的“病历摘要”AWR报告不是简单数据堆砌而是Oracle工程师基于海量采样数据生成的结构化诊断摘要。生成命令如下在数据库服务器执行# 进入SQL*Plus $ sqlplus / as sysdba SQL ?/rdbms/admin/awrrpt.sql交互式菜单中选择Report Type1HTML格式便于图文分析Num Days输入1聚焦问题当日Begin Snapshot Id/End Snapshot Id输入之前定位的SNAP_ID12345和12346Report Name自定义如awr_lock_report_20240615.html生成的HTML报告中重点关注三个章节Top 5 Timed Events若enq: TX - row lock contention长期位列Top 5说明锁问题已成系统性瓶颈。SQL Statistics SQL ordered by Elapsed Time找出执行时间最长的SQL它很可能是锁的源头如未加WHERE条件的全表UPDATE。Instance Activity Stats Enqueue activity查看TX队列的Requests、Waits、Wait Time (s)等指标量化锁争用的严重程度。5.2 ADDM自动给出“治疗方案”的AI医生ADDM是Oracle内置的AI诊断引擎它会自动分析AWR快照间的性能变化并生成带优先级的优化建议。启动方式同样简单-- 在SQL*Plus中执行 SQL ?/rdbms/admin/addmrpt.sql选择相同的时间范围后ADDM报告会明确指出Finding 1: High enq: TX - row lock contention WaitsImpact: 45% of database timeRecommendation: Application logic should be reviewed to ensure transactions are committed promptly after data modification. Consider adding COMMIT statements in PL/SQL blocks.Rationale: The analysis shows that session 456 held a TX lock for 32 seconds, blocking 17 other sessions.这份报告的价值在于它把技术现象锁等待翻译成了业务语言应用逻辑缺陷并给出了可落地的改进方向检查PL/SQL提交逻辑。这比手动分析ASH数据高出一个维度——它不仅告诉你“发生了什么”更告诉你“为什么发生”和“该怎么改”。实战经验我在一家电商公司处理过一个经典案例。ADDM报告指出“High TX lock waits due to uncommitted transaction in package PKG_ORDER_PROCESS”。顺着这个线索我们审查了该包的源码发现一个异常处理分支中遗漏了ROLLBACK语句。当订单处理遇到特定错误时事务既不提交也不回滚导致锁无限期持有。修复后同类故障下降98%。这印证了一个真理历史锁查询的终点永远是代码质量的起点。6. 预防胜于治疗构建锁问题的主动防御体系查到历史锁是救火而建立一套主动防御体系才是DBA职业价值的真正体现。这套体系不依赖复杂的工具而是由四个轻量级、高实效性的实践组成已在多个生产环境验证有效。6.1 会话级锁超时给每个事务装上“安全气囊”Oracle原生支持ALTER SESSION SET DDL_LOCK_TIMEOUT n但此参数仅对DDL生效。对DML锁需在应用层实现。最稳妥的方式是在所有可能产生锁的DML语句前加上FOR UPDATE WAIT n子句。例如-- 原始语句无超时可能无限等待 SELECT * FROM ORDERS WHERE ORDER_ID 1001 FOR UPDATE; -- 改进后等待5秒超时抛ORA-30006 SELECT * FROM ORDERS WHERE ORDER_ID 1001 FOR UPDATE WAIT 5;当应用捕获到ORA-30006时可优雅降级如提示用户稍后重试而非让整个线程卡死。这相当于给每个事务配了一个“安全气囊”即使代码有缺陷也能防止雪崩。6.2 锁监控告警让DBA比业务方更早感知基于ASH数据可构建分钟级锁监控脚本。以下是一个精简版保存为check_locks.sql-- 检查过去5分钟内是否存在单一会话锁等待超过10秒 SELECT COUNT(*) AS WAIT_COUNT, MIN(SAMPLE_TIME) AS FIRST_WAIT, MAX(SAMPLE_TIME) AS LAST_WAIT, SESSION_ID, SQL_ID FROM DBA_HIST_ACTIVE_SESS_HISTORY WHERE SAMPLE_TIME SYSTIMESTAMP - INTERVAL 5 MINUTE AND EVENT enq: TX - row lock contention GROUP BY SESSION_ID, SQL_ID HAVING MAX(SAMPLE_TIME) - MIN(SAMPLE_TIME) INTERVAL 10 SECOND;将此脚本加入CronLinux或Windows任务计划每5分钟执行一次。若返回结果非空则通过邮件或企业微信告警。我们曾用此脚本在业务方投诉前12分钟就发现了锁问题赢得了“神速响应”的口碑。6.3 开发规范嵌入把锁意识融入研发流程技术手段之外流程规范是治本之策。我们在数据库上线评审清单中强制加入两条锁风险评估所有涉及UPDATE/DELETE的SQL必须注明预期影响行数、WHERE条件选择性、是否在事务中执行。事务边界声明PL/SQL包中每个过程必须在注释中明确标注TransactionScope: [READ_ONLY | READ_WRITE]和CommitPolicy: [AUTO | MANUAL]。这些看似琐碎的要求让开发人员在编码阶段就思考锁的影响从源头减少问题。6.4 定期锁健康检查一份给自己的“体检报告”每月最后一个周五执行一次全面锁健康检查运行SELECT * FROM DBA_HIST_LOCK_ACTIVITY统计本月TX锁总次数对比上月数据若增长20%触发根因分析抽样检查Top 10锁等待SQL的执行计划确认是否存在全表扫描导致锁范围过大审查V$TRANSACTION中长时间未提交事务START_TIME早于24小时定位潜在隐患。这份“体检报告”不追求技术炫技但年复一年坚持下来所在系统的锁相关故障率下降了76%。因为预防的本质就是把偶然的“救火”变成必然的“维护”。我在实际运维中发现最有效的锁问题解决方案往往不是最复杂的SQL而是最朴素的实践一个WAIT 5一次定时巡检一条写在代码注释里的事务声明。技术会迭代工具会更新但对数据严谨的态度、对流程敬畏的心才是DBA职业生命的真正护城河。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价