Oracle 11g 数据库卡顿排查与实战优化:一次真实的慢 SQL 定位全过程

🚨 Oracle 11g 数据库卡顿排查与实战优化:一次真实的慢 SQL 定位全过程

背景

生产环境 Oracle 11g 数据库突然"很卡",应用接口响应慢、CPU 飙高、用户大量超时。

本文完整记录一次 从现象 → 定位 → 分析 → 处置 → 优化方案 的全过程,适合 DBA / 后端开发 / 运维参考。


一、问题现象

  • 应用大量接口响应慢
  • 数据库 CPU 使用率持续 90%+
  • 会话数正常,但 ACTIVE 会话长期不释放
  • Oracle 11g,无 AWR 授权(只能靠动态视图)

二、第一步:查看当前正在执行的慢 SQL(实时)

1️⃣ 查看执行超过 10 秒的 SQL

vbnet 复制代码
SELECT 
    s.sid,
    s.serial#,
    s.username,
    s.status,
    s.program,
    sq.sql_id,
    SUBSTR(sq.sql_text, 1, 100) AS sql_text,
    s.last_call_et AS 已执行秒数,
    s.event AS 等待事件,
    s.wait_time,
    s.seconds_in_wait
FROM v$session s
JOIN v$sql sq ON s.sql_id = sq.sql_id
WHERE s.status = 'ACTIVE'
  AND s.username NOT IN ('SYS', 'SYSTEM')
  AND s.last_call_et > 10
ORDER BY s.last_call_et DESC;

🔍 执行结果解读

  • 多个会话执行时间 几十秒 ~ 几分钟

  • 明显看到:

    • Table Scan: GSTPGG.CEN_CHECK_MEMBER
    • Index Fast Full Scan: GSTPGG.RISK_CLUE_BASIC_INFO

➡️ 已经可以初步判断:存在严重的全表扫描问题


三、第二步:查看"已执行完但仍在内存中"的慢 SQL

2️⃣ 从 v$sql 查历史慢 SQL

vbnet 复制代码
SELECT 
    sql_id,
    SUBSTR(sql_text, 1, 100) AS sql_text,
    executions AS 执行次数,
    ROUND(elapsed_time/1000000, 2) AS 总耗时_秒,
    ROUND(cpu_time/1000000, 2) AS CPU时间_秒,
    disk_reads AS 磁盘读取,
    buffer_gets AS 缓冲区获取,
    rows_processed AS 处理行数,
    last_load_time AS 最后加载时间
FROM v$sql
WHERE elapsed_time/1000000 > 5
  AND parsing_schema_name = 'GSTPGG'
ORDER BY elapsed_time DESC
FETCH FIRST 20 ROWS ONLY;

📊 关键发现

类型 对象 扫描方式
多条 SQL CEN_CHECK_MEMBER Table Scan(全表扫描)
少量 SQL RISK_CLUE_BASIC_INFO Index Fast Full Scan

➡️ 性能瓶颈明确:CEN_CHECK_MEMBER 表


四、第三步:定位正在执行的"长时间扫描任务"

3️⃣ 使用 v$session_longops 查看进度

vbnet 复制代码
SELECT 
    l.sid,
    l.serial#,
    s.sql_id,
    s.sql_text,
    l.opname,
    l.target,
    l.sofar,
    l.totalwork,
    ROUND(l.sofar/l.totalwork*100, 2) AS pct_complete,
    l.elapsed_seconds,
    l.time_remaining
FROM v$session_longops l
JOIN v$session se ON l.sid = se.sid AND l.serial# = se.serial#
LEFT JOIN v$sql s ON se.sql_id = s.sql_id
WHERE l.opname LIKE '%Scan%'
  AND l.username = 'GSTPGG'
  AND l.time_remaining > 0
ORDER BY l.start_time;

🚨 结果非常危险

  • 5 个会话同时
  • 同一张表 CEN_CHECK_MEMBER
  • 进行 并发全表扫描
  • 每个会话扫描进度仅 8% ~ 28%

➡️ 这会导致:

  • Buffer Cache 被疯狂挤占
  • 其他 SQL 全部变慢
  • 数据库"看起来像死了一样"

五、第四步:分析问题表本身(核心)

4️⃣ 表基本信息

ini 复制代码
SELECT 
    owner,
    table_name,
    num_rows,
    blocks,
    avg_row_len,
    last_analyzed
FROM dba_tables 
WHERE owner = 'GSTPGG'
  AND table_name = 'CEN_CHECK_MEMBER';

📊 表分析结果

指标 数值
行数 20,496,308 行
数据块 440,297 blocks
平均行长 149 字节
最近分析时间 3 天前

⏱️ 理论扫描耗时估算

bash 复制代码
440,297 blocks × 8KB ≈ 3.44GB
3.44GB ÷ 200MB/s ≈ 17 秒(理想)

⚠️ 现实情况:

  • 多会话并发
  • Buffer Cache 争抢
  • CPU 调度

➡️ 实际耗时:几十秒 ~ 数分钟


六、第五步:精准锁定"罪魁祸首 SQL"

5️⃣ 找出执行全表扫描的 SQL

ini 复制代码
SELECT 
    s.sid,
    s.serial#,
    s.sql_id,
    SUBSTR(q.sql_text, 1, 200) AS sql_text,
    s.program,
    s.last_call_et
FROM v$session s
JOIN v$sql q ON s.sql_id = q.sql_id
WHERE s.sql_id IN (
    SELECT sql_id
    FROM v$sql_plan
    WHERE object_name = 'CEN_CHECK_MEMBER'
      AND operation = 'TABLE ACCESS'
      AND options = 'FULL'
);

🔎 发现多个会话在反复执行同一条 SQL


七、第六步:打印完整 SQL(关键)

6️⃣ 使用 v$sqltext 还原完整 SQL

sql 复制代码
SELECT LISTAGG(sql_text, '') 
       WITHIN GROUP (ORDER BY piece) AS full_sql_text
FROM v$sqltext
WHERE sql_id = '9jdbd64yqbwcf';

✅ 完整 SQL 如下:

sql 复制代码
SELECT ...
FROM CEN_CHECK_MEMBER
WHERE (card_id LIKE :1 AND IS_DELETE = :2)

八、问题本质终于浮出水面

🚨 核心性能问题总结

❌ 1. LIKE 条件导致索引失效

  • card_id LIKE '%xxx%'
  • B-Tree 索引无法使用
  • Oracle 只能选择 全表扫描

❌ 2. 表太大 + 高频调用

  • 单表 2000 万行
  • SQL 执行 1599 次
  • 总逻辑读 6.67 亿次
  • 处理数据 2.85 亿行

❌ 3. 并发执行雪上加霜

  • 多个 JDBC 会话同时扫描
  • Buffer Cache 被"刷爆"

九、应急处理:立即止血

7️⃣ 生成批量 Kill 会话脚本

ini 复制代码
SELECT 
    'ALTER SYSTEM KILL SESSION ''' || s.sid || ',' || s.serial# || ''' IMMEDIATE;' AS kill_cmd
FROM v$session s
WHERE s.sql_id = '9jdbd64yqbwcf'
  AND s.username = 'GSTPGG'
  AND s.status = 'ACTIVE'
  AND s.last_call_et > 10;

⚠️ 仅用于应急,务必与业务确认


十、根本解决方案(重点)

✅ 方案一:创建正确索引

ini 复制代码
CREATE INDEX IDX_CEN_CHECK_MEMBER_CARD_DEL
ON GSTPGG.CEN_CHECK_MEMBER(card_id, IS_DELETE)
NOLOGGING PARALLEL 8;

索引生效前提

LIKE 写法 是否走索引
card_id LIKE '123%' ✅ 是
card_id LIKE '%123%' ❌ 否

✅ 方案二:业务 SQL 改造建议

sql 复制代码
-- 原(危险)
WHERE card_id LIKE '%123%'

-- 优化(可控)
WHERE card_id LIKE '123%'

✅ 方案三:分页 + 列裁剪

sql 复制代码
SELECT mem_number, mem_name, card_id
FROM CEN_CHECK_MEMBER
WHERE card_id LIKE :1
  AND IS_DELETE = 0
AND ROWNUM <= 50;

十一、经验总结(血的教训)

Oracle 慢 SQL 排查三板斧:

  1. v$session:看现在谁在跑
  2. v <math xmlns="http://www.w3.org/1998/Math/MathML"> s q l / v sql / v </math>sql/vsql_plan:看谁最耗资源
  3. 执行计划 + 表规模:判断是不是"不该扫却在扫"

🎯 最终结论

数据库不是"突然变慢"的,而是某条 SQL 在特定条件下被放大了 1000 倍。

  • 一次 %LIKE%
  • 一张 2000 万行表
  • 几个并发会话
    ➡️ 足以拖垮整个 Oracle 实例
相关推荐
Linux Huang2 小时前
spring注册组件/服务无效,问题排查
大数据·服务器·数据库·spring
SweetCode2 小时前
汉诺塔问题
android·java·数据库
superman超哥2 小时前
Rust Cargo Run 与 Cargo Test 命令:开发工作流的双引擎
开发语言·后端·rust·cargo run·cargo test·开发工作流·双引擎
MMM_FanLe2 小时前
微博/朋友圈/点赞/评论系统设计
后端
架构精进之路2 小时前
AI 编程:重构工作流的思维与实践
后端·ai编程·trae
橙汁味的风2 小时前
4数据库安全性
数据库·oracle
爬山算法2 小时前
Hibernate(9)什么是Hibernate的Transaction?
java·后端·hibernate
天竺鼠不该去劝架2 小时前
传统财务管理瓶颈:财务机器人如何提升效率
大数据·数据库·人工智能
码农爱学习2 小时前
嵌入式Linux利用core-dump文件和gdb工具分析程序崩溃问题
linux·数据库·postgresql