Automatic Indexing 从 19c 就有了,但 26ai 做了不少优化,比如支持分区表、支持函数索引、还能跟 AI 预测联动。我们开了半年,建了 400 多个自动索引,删了 200 多个,最后总结出三条血泪规律。
9.1 规律一:报表月的晚上,它一定会给你建一堆废索引
自动索引的学习期是 24 小时,但它不会区分业务高峰和低谷。我们有个客户每月 1 号凌晨跑 300 张报表,这些报表 SQL 在平常白天根本不跑。自动索引一看:"哟,这么多全表扫描,我得救一下。"于是凌晨 2 点哐哐建索引,白天 OLTP 来了,这些索引根本用不上,还占空间、拖慢 DML。我们的解决办法是:用 DBMS_AUTO_INDEX.CONFIGURE 把 REPORT_WINDOW 设成只监控白天时段,或者干脆给报表用户设 EXCLUDE_SCHEMA。
9.2 规律二:它看不懂隐式转换
有个经典场景:WHERE order_id = '12345',但 order_id 是 NUMBER 类型。Oracle 会自动做 TO_NUMBER(order_id) 的隐式转换,导致索引失效。自动索引检测到这条 SQL 走了全表扫描,就会建一个索引,但由于隐式转换的存在,建了也用不上,执行计划还是 FTS。更坑的是,自动索引的验证阶段(VERIFY)会跑 24 小时,如果这 24 小时内没人再跑那条 SQL,它就把索引标成 INVISIBLE,但你已经白等了 24 小时。
9.3 规律三:复合索引的顺序它经常猜错
-- 自动索引生成的复合索引(实际案例)
CREATE INDEX "SYS_AI_12345" ON orders (status, create_time, region);
-- 但业务查询的实际过滤条件是:
SELECT * FROM orders
WHERE create_time > SYSDATE - 7
AND region = '华东'
AND status = '已完成';
-- 自动索引把 status 放第一列,因为 status 的 distinct 值少(基数低),
-- 但查询里 create_time 的选择性更好,而且范围查询放第二列以后会导致后续列无法走索引。
-- 正确的索引应该是 (region, create_time, status)
-- 我们的干预方式:
BEGIN
DBMS_AUTO_INDEX.DROP_INDEX('SYS_AI_12345');
-- 然后手工建正确的索引
CREATE INDEX idx_orders_correct ON orders(region, create_time, status);
-- 把该 SQL 加入自动索引的黑名单
DBMS_AUTO_INDEX.CONFIGURE('AUTO_INDEX_BLACKLIST', 'orders%create_time%status');
END;
/
半年下来,我们的策略是:自动索引开着,但每天早会 DBA 要过一遍 DBA_AUTO_INDEXES 里 STATUS='VISIBLE' 的新索引,用 EXPLAIN PLAN 验证关键 SQL 是否真的在用。别做甩手掌柜,否则半年后你的库会膨胀 30%。