客户的一台 X9M-2,AWR 报告显示 Smart Scan 的 offload 率只有 58%,也就是说 42% 的 I/O 没下推到存储节点,回了数据库节点。Exadata 的核心价值就是 offload,这比例太低等于白买。我花了三天时间排查,最后发现是三个小问题的叠加。
10.1 问题一:Storage Index 的 1MB 粒度陷阱
Exadata 的 Storage Index 是在存储节点(Cell)上维护的,每个盘区(extent)1MB,记录这个 1MB 区间里某列的最大最小值。如果查询的谓词落在 Storage Index 的范围内,Cell 直接跳过这个 1MB,不读盘。但问题是,如果表的数据分布很均匀,每个 1MB 区间的最大最小值都差不多,Storage Index 就筛不掉什么。我们那个客户的订单表,create_time 是顺序插入的,每个 1MB 区间的时间跨度只有 2 小时,而查询条件是最近 7 天,结果几乎所有 1MB 区间都命中,Storage Index 完全失效。
10.2 问题二:Bloom Filter 没下推
星型模型的大表 join 维度表,理论上 Exadata 会把维度表的过滤条件做成 Bloom Filter,下推到存储节点,在扫描事实表时就过滤掉大部分行。但我们发现有个查询没走 Bloom Filter 下推,因为维度表用了 NVL 函数:WHERE NVL(dim.status, 'UNKNOWN') = 'ACTIVE'。这个函数导致优化器认为结果集不确定,不敢生成 Bloom Filter。改成 CASE WHEN 或者把 NULL 值在 ETL 阶段处理掉,offload 率立刻从 58% 涨到 82%。
10.3 问题三:HCC 压缩跟 offload 的博弈
-- 查看 offload 效率
SELECT sql_id,
io_cell_offload_eligible_bytes / 1024/1024/1024 eligible_gb,
io_cell_offload_returned_bytes / 1024/1024/1024 returned_gb,
(1 - io_cell_offload_returned_bytes/io_cell_offload_eligible_bytes) * 100 offload_pct
FROM v$sql
WHERE sql_text LIKE '%orders%'
AND executions > 10;
-- 查看 Storage Index 节省的 I/O
SELECT cell_name,
physical_read_bytes / 1024/1024/1024 read_gb,
io_saved_by_storage_index / 1024/1024/1024 saved_gb
FROM v$cell_state;
-- HCC 查询压缩率与 offload 关系
SELECT segment_name,
compression_ratio,
smart_scan_count,
offload_eligible_bytes / 1024/1024/1024 offload_gb
FROM dba_segments s, v$segment_statistics st
WHERE s.segment_name = 'ORDERS'
AND compression = 'QUERY HIGH';
第三个问题是 HCC(Hybrid Columnar Compression)的 QUERY HIGH 模式。压缩率确实高(10:1),但解压开销大,某些复杂谓词(比如正则、LIKE '%xxx%')解压后才能在存储节点评估,导致部分行必须回传数据库节点。我们把那张表改成 ARCHIVE HIGH(只读场景),offload 率又提了 8 个百分点。最终 offload 率从 58% → 89%,客户终于觉得 X9M 的钱没白花。