CBO是如何计算range谓词的选择率

简介:
\

      10月22日,由北极中亦安图陈宏义老师在“DBA+上海群”进行了一次关于“ CBO是如何计算range谓词的选择率”的线上主题分享。小编特别整理出其中精华内容,供大家学习交流。
 

 

嘉宾简介
 
 

 

陈宏义,10年开发,10年DBA,曾服务于SONY,ORACLE GCS,现在就职于北京中亦安图上海分公司

 

演讲实录
 

 

公式:

COL >= val谓词的选择率(无直方图)

((high_value - val)/(high_value - low_value)) * A4NULLS

A4NULLS为非空率

A4NULLS = (NUM_ROWS - NUM_NULLS)/NUM_ROWS

 

 

实验:

 

drop table t1;

create table t1 (id number);

declare

vid number;

begin

for i in 1..1000 loop

if mod(i,30) = 0 then

vid := null;

else

vid :=i;

end if;

insert into t1 values(vid);

end loop;

end;

 

exec dbms_stats.gather_table_stats(null,'T1');

col high_value for a20

col low_value for a20

 

select num_rows from user_tables where table_name = 'T1';

NUM_ROWS

----------

1000

 

select high_value,low_value,num_nulls,histogram from user_tab_columns where table_name='T1' and column_name='ID';

HIGH_VALUE LOW_VALUE NUM_NULLS HISTOGRAM

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

C20B C102 33 NONE

 

drop table t1;

create table t1 (id number);

declare

vid number;

begin

for i in 1..1000 loop

if mod(i,30) = 0 then

vid := null;

else

vid :=i;

end if;

insert into t1 values(vid);

end loop;

end;

 

exec dbms_stats.gather_table_stats(null,'T1');

col high_value for a20

col low_value for a20

 

select num_rows from user_tables where table_name = 'T1';

NUM_ROWS

----------

1000

 

select high_value,low_value,num_nulls,histogram from user_tab_columns where table_name='T1' and column_name='ID';

HIGH_VALUE LOW_VALUE NUM_NULLS HISTOGRAM

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

C20B C102 33 NONE

 

 

HIGH_VALUE , LOW_VALUE 可以用dbms_stats.conver_raw_value转换成可以读懂的数字,字符,日期。

 

这个案例里ID列的最小值是1,最大值是1000。

 

 

NUM_ROWS:1000

LOW_VALUE:1

HIGH_VALUE:1000

NUM_NULLS:33

HISTOGRAM: NONE

 

 

根据上面的公式,可以算出选择率:

 

 

谓词:ID>=700

A4NULLS=(1000-33)/1000=0.967

Selectivity=(1000-700+1)/(1000-1)*0.967=.291358358

Cardinality = 1000*.291358358 = 291.358358

 

最后得出的cardinality是291

 

四舍五入,最小值是1

 

 

用执行计划来验证一下:

 

 

数字可以简单地加减,但是字符串就没有办法了。

 

字符串是没有办法进行加、减运算的,Oracle会把它转换成一个内部的数字 ,这个转换算法在SQLT中公布出来。

 

SQLT中的函数名为:get_internal_value

 

Create or replace FUNCTION get_internal_value (p_value IN VARCHAR2)

 

RETURN VARCHAR2

 

IS

 

temp_n NUMBER := 0;

 

BEGIN

 

FOR i IN 1..15

 

LOOP

 

temp_n := temp_n + POWER(256, 15 - i) * ASCII(SUBSTR(RPAD(p_value, 15, CHR(0)), i, 1));

 

END LOOP;

 

RETURN TO_CHAR(ROUND(temp_n, -21));

 

EXCEPTION

 

WHEN OTHERS THEN

 

RETURN p_value;

 

END get_internal_value;

 

这是用pl/sql实现的转换字符串为数字的算法。CBO在算的时候,一定不是用pl/sql是用C

 

例如,

 

Sample,

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

SQL> select get_internal_value('AAAAA') from dual;

 

GET_INTERNAL_VALUE('AAAAA')

 

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

 

338822822454670000000000000000000000

 

CBO在算>,< between这类谓词时用的就是这个数字。

 

这个算法,不具有唯一性,经常会不同的字符串得到相同的internal_value, 如果因为这个问题导致了数据分布倾斜,Oracle会为这个列收集直方图,并用endpoint_actaul_value来校准

 

 

drop table t1;

 

drop table t2;

 

create table t1(id number primary key,cdate varchar2(20));

 

create table t2(id number primary key,cdate varchar2(20));

 

create index t1_cdate on t1(cdate);

 

declare

 

s date;

 

begin

 

s := to_date('2015-03-23 00:00:00','yyyy-mm-dd hh24:mi:ss');

 

for i in 1..10000 loop

 

insert into t1 values(i,to_char(s,'yyyy-mm-dd hh24:mi:ss'));

 

insert into t2 values(i,to_char(s,'yyyy-mm-dd hh24:mi:ss'));

 

s := s+0.1;

 

end loop;

 

commit;

 

end;

 

/

 

exec dbms_stats.gather_table_stats(null,'T1',method_opt=>'for all columns size 1');

 

exec dbms_stats.gather_table_stats(null,'T2',method_opt=>'for all columns size 1');

 

alter session set statistics_level=all;

 

select * from t1,t2 where t1.id = t2.id and

 

t1.cdate between '2015-06-23 00:00:00'

 

and '2015-09-21 23:59:59'

 

and t2.cdate between '2015-09-14 14:24:00' and '2015-09-15 14:24:00';

 

select * from table(dbms_xplan.display_cursor(null,null,'ALLSTATS'));

 

这是sample的代码

 

从上面的执行计划中可以看出,T1表通过谓词t1.cdate between '2015-06-23 00:00:00' and '2015-09-21 23:59:59'的过滤得到910行数据,而CBO估算它1行。

 

T2表通谓词t2.cdate between '2015-09-14 14:24:00' and '2015-09-15 14:24:00'的过滤,得到11行数据,而CBO却估算成910行.

 

由于这个估算偏差导致错误地使用了T1作为nested loop的驱动表。

 

我们看一下'2015-06-23 00:00:00','2015-09-21 23:59:59'这两个字符串,化成的internal value是什么。

 

'2015-06-23 00:00:00' = 260592297225015000000000000000000000

'2015-09-21 23:59:59' = 260592297225015000000000000000000000

由于这两个值是相同的,所以这个条件,就相当于=了。所以计算出的cardinality就非常低。

 

解决方案:

1. 收集直方图;

2. 将varchar2型改成date型。

 

 

收集直方图后,执行计划变为:

 


 

另外,dba_histograms.endpoint_actaul_value这个列就是干这个用的。校准 internal value重复的情况。

 

本文来自云栖社区合作伙伴"DBAplus",原文发布时间:2015-10-23

 
目录
相关文章
|
数据库 数据安全/隐私保护
TiDB分布式事务处理机制
【2月更文挑战第28天】TiDB作为开源的分布式HTAP数据库产品,其分布式事务处理机制是其核心功能之一。本章节将深入解析TiDB分布式事务处理机制的工作原理,包括其采用的分布式事务协议、事务的提交与回滚过程、以及如何处理并发事务等关键内容。通过了解TiDB的分布式事务处理机制,我们可以更好地理解其在分布式环境下如何确保数据一致性和事务正确性。
|
缓存 自然语言处理 物联网
LLama Factory+ModelScope实战——使用 Web UI 进行监督微调
LLaMA Factory 是一个高效的大语言模型训练和推理框架,它通过提供一站式的 Web UI 界面和集成多种训练方法,简化了大模型的微调过程,并能够适配多种开源模型。
|
小程序 开发工具 开发者
微信开发者工具使用教程
微信开发者工具使用教程
|
关系型数据库 分布式数据库 数据库
学习分布式不得不会的ACP理论
2000年7月,加州大学伯克利分校的Eric Brewer教授在ACM PODC会议上提出CAP猜想。2年后,麻省理工学院的Seth Gilbert和Nancy Lynch从理论上证明了CAP。
3263 0
|
存储 数据采集 Prometheus
深入调查研究
【10月更文挑战第28天】
222 2
|
9月前
|
机器学习/深度学习 运维 自然语言处理
深度学习+实时监控:运维不再靠“拍脑袋”!
深度学习+实时监控:运维不再靠“拍脑袋”!
375 3
|
监控 安全 数据安全/隐私保护
|
Unix Shell 数据处理
怎样使用Cython提升Python的性能
**Cython是Python的性能增强工具,用于提升Python代码的速度。它允许声明变量类型并调用C库。安装Cython使用`pip install Cython`。Cython语法接近Python,但通过类型声明优化性能。编译Cython代码需创建setup.py文件,然后运行`python setup.py build_ext --inplace`。通过Cython,可以直接优化Python代码和调用C函数,平衡速度与灵活性。**
444 2
|
SQL 关系型数据库 分布式数据库
数据库索引,一文揭秘PolarDB XPlan索引选择
深度解读PolarDB分布式版XPlan的索引选择
数据库索引,一文揭秘PolarDB XPlan索引选择
|
数据采集 机器人 计算机视觉
一手训练,多手应用:国防科大提出灵巧手抓取策略迁移新方案
【10月更文挑战第24天】国防科技大学研究人员提出了一种新颖的机器人抓取方法,通过学习统一的策略模型,实现不同灵巧夹具之间的策略迁移。该方法分为两个阶段:与夹具无关的策略模型预测关键点位移,与夹具相关的适配模型将位移转换为关节调整。实验结果显示,该方法在抓取成功率、稳定性和速度方面显著优于基线方法。论文地址:https://arxiv.org/abs/2404.09150
203 1

热门文章

最新文章