Oracle优化器 Cardinality基数与Selectivity选择性

本文深入解析Oracle数据库中的两种优化器:基于规则的优化器(RBO)和基于成本的优化器(CBO)。RBO通过预设等级选择执行路径,适用于OLTP场景;CBO则根据统计信息计算执行成本,选择成本最低的执行计划,更适用于复杂查询。

      优化器(optimizer)是oracle数据库内置的一个核心子系统。优化器的目的是按照一定的判断原则来得到它认为的目标SQL在当前的情形下的最高效的执行路径,也就是为了得到目标SQL的最佳执行计划。依据所选择执行计划时所用的判断原则,oracle数据库里的优化器又分为RBO和CBO两种。

一、基于规则的优化器。《RBO: Rule-Based Optimization》

     Oracle会在代码里事先为各种类型的执行路径定一个等级,一共15个等级,从等级1到等级15,oracle认为等级1的执行路径是效率最高的,等级15是执行效率最差的。对于等级相同的执行计划,oracle根据目标对象的在数据字典中缓存的顺序判断选择哪一种执行计划。RBO是一种适合于OLTP类型SQL语句的优化器。相对于CBO而言,RBO有着先天的缺陷,一旦SQL语句的执行计划出现问题,将很难调整。因此,从ORACLE 10g开始,RBO已被CBO所取代

    关于RBO的访问路径,官方文档做了详细介绍:

        RBO Path 1: Single Row by Rowid

RBO Path 2: Single Row by Cluster Join

RBO Path 3: Single Row by Hash Cluster Key with Unique or Primary Key

RBO Path 4: Single Row by Unique or Primary Key

RBO Path 5: Clustered Join

RBO Path 6: Hash Cluster Key

RBO Path 7: Indexed Cluster Key

RBO Path 8: Composite Index

RBO Path 9: Single-Column Indexes

RBO Path 10: Bounded Range Search on Indexed Columns

RBO Path 11: Unbounded Range Search on Indexed Columns

RBO Path 12: Sort Merge Join

RBO Path 13: MAX or MIN of Indexed Column

RBO Path 14: ORDER BY on Indexed Column

RBO Path 15: Full Table Scan

二、基于成本的优化器。《 CBO: Cost-Based Optimization》

       CBO是一种比RBO更加合理、可靠的优化器,它是从ORACLE 8中开始引入,但到ORACLE 9i 中才逐渐成熟,在ORACLE 10g中完全取代RBO。 CBO选择执行计划时,以目标SQL成本为判断原则,CBO会选择一条执行成本最小的执行计划作为SQL的执行计划,各条执行路径的成本通过目标SQL语句所涉及的表、索引、列等的统计信息算出。这里的成本是oracle通过相关对象的统计信息计算出来的一个值,它实际上代表目标SQL对应执行步骤所消耗的IO、CPU、网络资源(针对于dblink下的分布式数据库系统而言)的消耗量,oracle会把网络资源的消耗量计算在IO成本内,实际上你看到的成本为IO、CPU资源,另外需要注意的是,oracle在未引入系统统计信息之前,CBO所计算的成本值实际全是基于IO计算的。

1、Cardinality(基数,集的势)

      是Oracle预估的返回行数,即对目标SQL的某个具体执行步骤的执行结果所包含记录数的估算值。如果是针对整个目标SQL,那么此时的Cardinality就表示该SQL最终执行结果所包含记录数的估算值。例如,一张表T有1000行数据,列COL1上没有直方图,没有空值,并且不重复的值(Distinct Value)有500个。那么,在使用条件“WHERE COL1=”去访问表的时候,优化器会假设数据均匀分布,它估计出会有1000/500=2行被选出来,2就是这步操作的Cardinality。通常情况下,Cardinality越准确,生成的执行计划就会越高效。

2、Selectivity(可选择率)是指施加指定谓词条件后返回结果集的记录数占未施加任何谓词条件的原始结果集的记录数的比率。可选择率的取值范围显然是0~1,它的值越小,就表明可选择性越好。当可选择率为1时的可选择性是最差的。

 Selectivity=1  /   NUM_DISTINCT(目标列的DISTINCT的数量,此为最简单的计算可选择率的情况,在目标列上没有直方图,且没有NULL时的公式)

 Cardinality=NUM_ROWS   *   Selectivity

---------------------------------------------------------------------------------------------------------

但Oracle在文档中,有时用基数来指一个操作返回的记录行数,有时也将基数指不重复的值的数量,因此基数与选择性在《SQL优化核心思想》中也有如下解释:

1、Cardinality(基数)某个列唯一键的数量(某列中不重复的值的数量,DISTINCT的数量),比如性别列,该列只有男女之分,所以这一列基数是2,基数越高与总行数越接近,数据分布可能会越均匀。

2、Selectivity(选择性)基数与总行数的比值再乘以100%,为某列的选择性(选择性大于20%,数据分布可能会越均衡)。

基数估计(Cardinality Estimation)是数据库查询优化中的一个关键环节,它直接影响查询计划的生成和执行效率。基数估计的目标是预测查询操作后结果集的行数(即基数),这对于选择最优的执行计划至关重要。以下是一些基数估计相关的技术原理和算法: ### 基数估计的基本原理 基数估计是查询优化器在生成执行计划时的重要依据。优化器需要根据表的统计信息(如行数、列的分布等)来估计每个操作的结果大小,从而选择最高效的执行路径。例如,在连接操作中,优化器需要估计连接结果的基数,以决定使用哪种连接算法(如嵌套循环连接、哈希连接或排序合并连接)[^2]。 ### 常见的基数估计算法 1. **直方图(Histogram)** 直方图是一种常用的统计信息表示方法,用于描述数据的分布情况。通过直方图,优化器可以更准确地估计查询条件的选择率,从而得到更精确的基数估计。直方图可以分为等宽直方图、等频直方图和核密度估计直方图等[^3]。 2. **采样(Sampling)** 采样是一种通过从数据集中随机抽取一部分数据来估计整体分布的方法。通过采样,优化器可以在不扫描整个表的情况下,得到较为准确的基数估计。然而,采样的精度受限于样本的大小和分布[^3]。 3. **多维统计(Multi-dimensional Statistics)** 多维统计方法通过考虑多个列之间的相关性来提高基数估计的精度。例如,某些数据库系统会维护列组(Column Group)的统计信息,以捕捉多列之间的联合分布。 4. **机器学习方法** 近年来,机器学习技术被引入到基数估计中。通过训练模型来预测查询结果的基数,机器学习方法可以在某些场景下提供比传统统计方法更高的精度。例如,使用神经网络模型来学习查询模式和数据分布之间的关系[^2]。 ### 基数估计的挑战 基数估计面临的主要挑战包括: - **数据分布的复杂性**:实际数据的分布往往不是均匀的,且可能存在偏斜或异常值,这使得传统的统计方法难以准确估计基数。 - **多列条件的处理**:当查询涉及多个列的条件时,如何准确捕捉列之间的相关性是一个难题。 - **动态数据的变化**:随着数据的频繁更新,统计信息可能变得过时,导致基数估计的不准确[^3]。 ### 基数估计在查询优化中的应用 在查询优化中,基数估计直接影响执行计划的选择。例如,在连接操作中,优化器会根据基数估计选择合适的连接算法。如果估计的基数较小,优化器可能会选择嵌套循环连接;如果基数较大,哈希连接或排序合并连接可能更为合适。此外,基数估计还影响索引的选择和排序操作的代价估算。 ### 示例代码:基于直方图的基数估计 以下是一个简单的基于直方图的基数估计示例代码,假设我们有一个包含数值数据的列,并使用等宽直方图来估计查询条件的选择率: ```python def estimate_cardinality_with_histogram(data, query_range, num_bins): import numpy as np # 创建等宽直方图 hist, bin_edges = np.histogram(data, bins=num_bins) # 计算查询范围内的桶索引 start_bin = np.digitize(query_range[0], bin_edges) end_bin = np.digitize(query_range[1], bin_edges) # 计算查询范围内的总行数 total_rows = sum(hist[start_bin-1:end_bin]) return total_rows # 示例数据 data = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] query_range = (3, 7) num_bins = 5 # 估计基数 cardinality = estimate_cardinality_with_histogram(data, query_range, num_bins) print(f"Estimated cardinality: {cardinality}") ``` ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值