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 s q l / v sql / v sql/vsql_plan:看谁最耗资源
  3. 执行计划 + 表规模:判断是不是"不该扫却在扫"

🎯 最终结论

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

  • 一次 %LIKE%
  • 一张 2000 万行表
  • 几个并发会话
    ➡️ 足以拖垮整个 Oracle 实例
相关推荐
Chenyiax13 分钟前
从 Chat 到 Responses:OpenAI API 抽象为什么变了?
后端
MariaH15 分钟前
Koa和Express的区别
后端
MariaH20 分钟前
Koa框架的使用
后端
luckdewei1 小时前
那个用 passlib 做认证的新同事,上线第一天就把用户密码写进了日志
后端
ping某3 小时前
为什么 Nginx 明明监听了 80,转发后端时却用了 4xxxx 端口?
后端·nginx
JustHappy3 小时前
我汇总了身边朋友的经历才发现,其实第一份实习是最难找的......
前端·后端·面试
uhakadotcom3 小时前
在python 的 工程化架构中 ,什么是 薄包装器层?
后端·面试·github
倔强的石头_7 小时前
《Kingbase护城河》——数据库存储空间全景探测与精细化瘦身实战
数据库
用户1474853079747 小时前
CodeX使用Skill生成游戏美术和音乐资源,一分钟入门
后端
Melody1237 小时前
用 abort 中断 AI 流式请求,我之前做错了
后端