通过索引访问数据的操作是执行计划中的一个基本操作,尤其是在 OLTP 系统中,大多数数据的查找、访问都是通过索引获取的。因此,索引的相关统计数据对于优化器估算出一个高效的执行计划也是起着至关重要的作用。下文将分析的普通(B*Tree 结构)索引统计数据的相关计算方法。
取样比
同样,在分析索引统计数据时,也可以指定取样比。如果取样比小于 100 的话,Oracle 会由索引占用的数据块数(NBLKS,由内部 API 获得)重新计算其采样比:
PCT := (DSC_MIN_MINMAX_SSIZE * 1.25 * 100)/ NBLKS;其中,DSC_MIN_MINMAX_SSIZE 为分析最大、最小值时要求的最小样本记录数,Oracle经验值,919。如果收集索引统计数据时,用户指定了取样百分比,则取用户指定百分比与该计算百分比中最大一个。
示例,当指定百分比分析索引 BIGTAB_IDX1,其调整取样百分比的计算:

索引数据块数为 1332,其调整后的取样百分比为: PCT := (919 * 1.25 * 100)/ 1332 = 86.2424924925
提示:分析索引统计数据是采用数据块取样而非数据记录取样。
基本统计数据
索引的几个基本统计数据,索引的数据记录数(Number of Rows,NRW)、索引树的叶子数据块数(Number of Leaf Blocks,NLB)、索引键值数(Number of Distinct Keys,NDK)和簇集因子(Clustering Factor,CLF),都可以通过查询语句获得。以下是从索引分析过程中跟踪获得的语句:

在语句中,Oracle 使用了几个内部函数用于获取几个基本的统计数据:
• sys_op_lbid:用于获取索引叶子数据块的 ID 号(即一个精度到数据块编号的 ROWID),在这里用于索引叶子数据块数的统计。
o 第一个参数是索引对象的编号;
o 第二个参数为数据块类型,'L’表示精确到叶子数据块(对于位图索引、索引组织表还 有其他选项);
o 第三到 N 个参数为一个索引字段或者索引记录中的 ROWID。使用该函数时,必须确保通过索引访问数据,因此在语句中存在提示
index(t,“T_OBJECTS_IDX1”);并且函数参数不能为绑定变量。
• sys_op_descend:获取一个数据的 16 进制数。在这里,Oracle 利用该函数将索引字段转换并拼接成一个 16 进制数,从而形成一个索引虚拟的键值(Dummy Key),用于对索引键值数的统计;
如果分析索引的同时,也分析了其索引字段的统计数据,则这些字段中最小的唯一值数
(NDV)就限制了索引的唯一键值数。而如果索引只包含一个字段,则这个字段的 NDV 就会成为索引的 NDK。
• sys_op_countchg:这是一个聚集函数,用于统计前后数据记录发生变化的次数。在这里,
Oracle 使用 substrb(t.rowid,1,15)作为参数。而从我们之前对 ROWID 的结构定义解释可知, 它将 ROWID 的行号去掉了,从而得到了数据块的变化。而语句中的提示使得该语句是通过索引完全扫描访问数据。我们知道,索引完全扫描是按照索引的树状结构逐个访问索引数据块,也就是说,它也是按照索引定义的顺序获取到索引记录以及记录中指向表数据的
ROWID。因此,函数 sys_op_countchg 得到结果也就是索引记录按顺序查找表数据块时,发生寻址跳跃的次数,这也就是簇集因子的定义。
样本数据放缩
如果数据由取样数据获得,则需要由取样比计算出实际值。
• 索引数据记录数(样本数据记录数 SSIZE 也就是样本中的索引数据记录数 SNRW)、索引叶子数据块数和簇集因子直接做线性放缩计算:
NRW := ROUND( SSIZE * 100 / PCT ) NLB := ROUND(SNLB * 100 / PCT); CLF := ROUND(SCLF * 100 / PCT);
• 索引键值数(Number of Distinct Keys,NDK)
o 如果索引为唯一索引(Unique Index),NDK 等于索引数据记录数 NRW。
o 对于非唯一索引,按照以下方法进行放缩计算:
如果满足 KKESDV 放缩条件,则进行 KKESDV 放缩;
如果满足线性放缩条件,则进行线性放缩;
NDK := SDK * 100 / PCT;
如果满足条件 (SDK / CHUNKS >= 3) 则按照以下公式放缩,
NDK := ((SDK - CHUNKS) * 100 / PCT) + CHUNKS;
其中,CHUNKS 由以下公式计算得出:
SMBRC := GREATEST(1,
ROUND(PCT * NBLKS /
(DSC_MIN_MINMAX_SSIZE * 100 * 1.25)));
CHUNKS := SNLB * SMBRC;
如果上述条件都不满足,则需要调整取样比重新分析;
计算统计数据
在索引统计数据中,除了上述几个由查询语句分析得出的几个数据外,还有两个重要的统计数 据,平均单个键值占用索引叶子数据块数(Average Leaf Blocks per Key,ALBK)和平均单个键值的索引记录指向的表数据块数(Average Data Blocks per Key,ADBK),则由这些基本统计数据计算得出。这两个数据对于优化器对非唯一索引(Non-unique Index)的相关代价估算起着重要作用。
• 对于唯一索引,每个键值只有一条索引记录,并且,Oracle 会根据数据块大小对索引的最大长度做限制,使之不能超出单个数据块的存储范围。因此,一个唯一索引键值的索引记 录只会占用到一个索引数据块,这两个数据的数值都为 1。
ALBK := 1;
ADBK := 1;
• 对于非唯一索引,这两个数据则由上述基础数计算得出:
ALBK := GREATEST(FLOOR(NLB / NDK), 1); ADBK := GREATEST(FLOOR(CLF / NDK), 1);
校验和修正
叶子数据块数、唯一键值数和簇集因子都不能大于索引数据记录数,否则,必须重置为索引数 据记录数。
对于唯一键值数,如果分析索引的同时,也分析了其索引字段的统计数据,则这些字段中最小 的唯一值数(NDV)就限制了索引的最小唯一键值数。




