查不到px等待事件需用gv$视图,因v$ash仅记录本实例采样;rac下px进程跨节点运行,单实例视图会遗漏关键等待;必须用gv$active_session_history并带inst_id分析,结合time_waited而非sample_count定位真实倾斜。
查不到px等待事件?先换gv$视图
在rac环境下,v$active_session_history只记录本实例采样,而并行查询的协调进程(px coordinator)和从进程(px slave)可能跨节点运行。若只查单实例视图,px deq: execution message、parallel query dequeue wait这类关键等待根本不会出现。
必须用gv$active_session_history,且建议显式带上inst_id比对:
SELECT inst_id, sql_id, event, COUNT(*) cnt FROM gv$active_session_history WHERE sample_time > SYSDATE - 1/1440 AND (event LIKE 'PX %' OR event LIKE 'parallel %') GROUP BY inst_id, sql_id, event ORDER BY cnt DESC;
- 若某
sql_id的等待集中在单个inst_id,说明并行任务没跨节点,问题更可能是本地CPU或I/O瓶颈 - 若等待在多个
inst_id上分散,但time_waited差异极大(比如节点2平均800ms,节点1仅5ms),就是典型的工作分配不均
别只看sample_count,盯紧time_waited
ASH每秒采样一次,COUNT(*)高只代表“被采到的次数多”,不代表真卡那么久。真正暴露倾斜的是time_waited(单位:微秒),它来自底层等待事件的精确计时。
以下语句能快速定位长尾延迟:
SELECT sql_id, event,
ROUND(AVG(time_waited)/1000, 2) avg_ms,
MAX(time_waited)/1000 max_ms
FROM v$active_session_history
WHERE event IN ('PX Deq: Execution Message',
'PX Deq: Table Q Normal',
'PX qref latch')
AND sample_time > SYSDATE - 1/24
GROUP BY sql_id, event
HAVING MAX(time_waited) > 5000000
ORDER BY max_ms DESC;
-
MAX(time_waited) > 5000000(即5秒)是硬信号:至少有一个PX slave卡住,拖慢整个并行组 -
AVG(time_waited)低但MAX极高,基本可断定是数据分布倾斜或分区裁剪失效,导致某个slave扫描远超其他slave
分区设计本身就在加剧倾斜?检查RANGE分区键
RANGE分区按键值区间物理划分,不考虑实际数据分布。当业务写入集中(如新订单全落最新分区、活跃用户ID集中在某段),就会出现「1%分区存70%数据」——I/O、锁、并行任务全压在少数几个分区上。
- 典型翻车场景:
ORDER BY order_id RANGE(order_id),但爆款商家单日占60%订单 - 更隐蔽的问题:
LIST PARTITION BY status,而status只有'PENDING'/'DONE'两值,80%是PENDING - 验证方式:
SELECT PARTITION_NAME, NUM_ROWS FROM DBA_TAB_PARTITIONS WHERE TABLE_NAME = 'YOUR_TABLE',看行数标准差是否超5倍
组合分区不是万能胶,得按访问模式配
单一维度分区扛不住热点时,组合分区(Composite Partitioning)是务实选择,但不能套模板。核心是拆解高频查询条件:
- 如果常带时间+用户ID查询,就用
RANGE-HASH:PARTITION BY RANGE (order_date) SUBPARTITION BY HASH (user_id) SUBPARTITIONS 8——每月一个主分区,内部再分8个子分区,避免单月数据爆炸后无法并行 - 别滥用
LIST-HASH:若LIST键本身低基数(如product_type IN ('A','B','C')),子分区再HASH也救不了整体倾斜 - DML影响容易被忽略:
INSERT /*+ APPEND */在组合分区下可能触发大量子分区维护开销,需实测吞吐量
真正难的不是发现倾斜,而是判断它是统计信息不准、分区设计缺陷,还是SQL写法本身导致裁剪失效——三者现象相似,但修复路径完全不同。查current_obj#关联dba_objects确认正在访问的表,再回溯它的分区策略和统计收集方式,才能切中要害。











