如何获得正确的基数估计值

原文:http://www.oracle.com/technetwork/issue-archive/2013/13-jan/o13asktom-1886639.html

I have a question about joining with collections and cardinality estimation. The optimizer is always estimating that 8,168 rows are coming back from my collection, and because of that, it is using inefficient plans. The estimate of 8,168 is more than two orders
of magnitude more than the real cardinality. How can I solve this problem?

我有一个关于集合和基数估计。优化器针对我的集合基数估计总是8168,就因为这,它老师使用低效率计划。8168是实际基数的2个数量级!

我该如何解决这个问题?

This is a long-running issue with pipelined functions and collections during optimization. The optimizer in general doesn’t have any information about the cardinality (the number of rows) being returned by the collection. It has to guess—and that guess is based
on the block size (default statistics are block-size-driven). So, for a database with an 8 K block size, the guess is about 8,000. And because it is unlikely that your collection has about 8,000 elements (probably more like 8 or 80 in most cases), you can
definitely arrive at a suboptimal plan.

这的确是个长期提出的问题:关于管道函数和集合的优化。优化器通常对于集合的基数信息毫无所知。它必须去猜!怎么猜?是基于块大小。

所以,对于8k大小数据块的数据库,猜出来的基数就是8000左右!但是通常集合的基数并不是8000,其更可能是8或80。

下面,我提供4种方法来获得正确的集合基数:

1)The cardinality hint (undocumented) 基数提示(无文件证明)

2)The OPT_ESTIMATE hint (undocumented) OPT_ESTIMATE提示(无文件证明)

3)Dynamic sampling (Oracle Database 11g Release 1 and later) 动态采样(Oracle 11g第一版或更高)

4)Oracle Database’s Cardinality Feedback feature (Oracle Database 11g Release 2 and later) Oracle基数反馈特性(Oracle 11g第2版或更高)

--1)用于字符串分割的管道函数

SQL> create or replace type str2tblType as table of varchar2(30)

/

Type created.

SQL> create or replace

function str2tbl( p_str in varchar2, p_delim in varchar2 default ‘,‘ )

return str2tblType

PIPELINED

as

l_str      long default p_str || p_delim;

l_n        number;

begin

loop

l_n := instr( l_str, p_delim );

exit when (nvl(l_n,0) = 0);

pipe row( ltrim(rtrim(substr(l_str,1,l_n-1))) );

l_str := substr( l_str, l_n+1 );

end loop;

end;

/

Function created.

SQL> variable x varchar2(15)

SQL> exec :x := ‘1,2,3,a,b,c‘

PL/SQL procedure successfully completed.

SQL> select * from table(str2tbl(:x));

COLUMN_VALUE

——————————————————————————————————————

1

2

3

a

b

c

6 rows selected.

SQL> select * from table(dbms_xplan.display_cursor);

PLAN_TABLE_OUTPUT

————————————————————————————————————————————————————————

SQL_ID  ddk1tv9s5pzq5, child number 0

————————————————————————————————————————————————————————

select * from table(str2tbl(:x))

Plan hash value: 2407808827

———————————————————————————————————————————————————————————————————————————

|Id|Operation                      |Name   |Rows|Bytes|Cost (%CPU)|Time    |

———————————————————————————————————————————————————————————————————————————

| 0|SELECT STATEMENT               |       |    |     |  29  (100)|        |

| 1| COLLECTION ITERATOR PICKLER...|STR2TBL|8168|16336|  29    (0)|00:00:01|

--使用CARDINALITY 提示

SQL> select * from table(dbms_xplan.display_cursor);

PLAN_TABLE_OUTPUT

————————————————————————————————————————————————————————

SQL_ID  bd2f8rh30z3ww, child number 0

————————————————————————————————————————————————————————

select /*+ cardinality(sq 10) */ * from table(str2tbl(:x)) sq

Plan hash value: 2407808827

———————————————————————————————————————————————————————————————————————————

|Id|Operation                      |Name   |Rows|Bytes|Cost (%CPU)|Time    |

———————————————————————————————————————————————————————————————————————————

| 0|SELECT STATEMENT               |       |    |     |  29  (100)|        |

| 1| COLLECTION ITERATOR PICKLER...|STR2TBL|  10|   20|  29    (0)|00:00:01|

--这地方,我们给优化器提示一个10使其认为管道函数的结果基数为10

--2)使用OPT_ESTIMATE提示

--提供3个参数:对象类型;对象名;一个缩放因素,优化器将拿它认为的基数乘以这个因素

--所以,先算出这个大概的因素

SQL> select 10/8168 from dual;

10/8168

————————————————

.00122429

select /*+ opt_estimate(table, sq, scale_rows=0.00122429) */ *

from table(str2tbl(:x)) sq

Plan hash value: 2407808827

———————————————————————————————————————————————————————————————————————————

|Id|Operation                      |Name   |Rows|Bytes|Cost (%CPU)|Time    |

———————————————————————————————————————————————————————————————————————————

| 0|SELECT STATEMENT               |       |    |     |  29  (100)|        |

| 1|  OLLECTION ITERATOR PICKLER...|STR2TBL|  10|   20|  29    (0)|00:00:01|

--3)使用DYNAMIC SAMPLING 提示

select /*+ dynamic_sampling( sq, 2 ) */ * from table( str2tbl(:x,‘,‘) ) sq

Plan hash value: 2407808827

———————————————————————————————————————————————————————————————————————————

|Id|Operation                      |Name   |Rows|Bytes|Cost (%CPU)|Time    |

———————————————————————————————————————————————————————————————————————————

| 0|SELECT STATEMENT               |       |    |     |  11  (100)|        |

| 1| COLLECTION ITERATOR PICKLER...|STR2TBL|   6|   12|  11    (0)|00:00:01|

———————————————————————————————————————————————————————————————————————————

Note

———————

dynamic sampling used for this statement (level=2)

--4)使用Cardinality Feedback

--这地方我们要将查询变一下形:

with sq

as (

select /*+ materialize */ *

from table( str2tbl( :x ) )

)

select *

from sq

Plan hash value: 630596523

—————————————————————————————————————————————————————————————————————————————

|Id|Operation                        |Name   |Rows|Bytes|Cost (%CPU)|Time    |

—————————————————————————————————————————————————————————————————————————————

| 0|SELECT STATEMENT                 |       |    |     |  32  (100)|        |

| 1| TEMP TABLE TRANSFORMATION       |       |    |     |           |        |

| 2|  LOAD AS SELECT                 |       |    |     |           |        |

| 3|   COLLECTION ITERATOR PICKLER...|STR2TBL|8168|16336|   29   (0)|00:00:01|

| 4|  VIEW                           |       |8168| 135K|    3   (0)|00:00:01|

| 5|   TABLE ACCESS FULL             |SYS_...|8168|16336|    3   (0)|00:00:01|

—————————————————————————————————————————————————————————————————————————————

18 rows selected.

--whoop,第一次还没生效呢,没关系, one more try

with sq as (select /*+ materialize */ *    from table( str2tbl( :x ) )

) select * from sq

Plan hash value: 630596523

—————————————————————————————————————————————————————————————————————————————

|Id|Operation                        |Name   |Rows|Bytes|Cost (%CPU)|Time    |

—————————————————————————————————————————————————————————————————————————————

| 0|SELECT STATEMENT                         |    |     |  32  (100)|        |

| 1| TEMP TABLE TRANSFORMATION               |    |     |           |        |

| 2|  LOAD AS SELECT                         |    |     |           |        |

| 3|   COLLECTION ITERATOR PICKLER...|STR2TBL|8168|16336|  29    (0)|00:00:01|

| 4|  VIEW                                   |   6| 102 |   3    (0)|00:00:01|

| 5|   TABLE ACCESS FULL             |SYS_...|   6|   12|   3    (0)|00:00:01|

—————————————————————————————————————————————————————————————————————————————

Note

———————

- cardinality feedback used for this statement

22 rows selected.

--这一次OK了

--揭晓一下基数反馈的原理,下面直接引用TOM的话:

Cardinality Feedback works by having the optimizer change its cardinality estimates after executing a query for the first time and observing that the actual cardinalities were very far off from the estimated cardinalities. That is, the optimizer starts to learn
from its mistakes. If it executes a query and discovers that the real row counts are far off from the estimated counts, it will reoptimize the query, using the newly discovered values.

关于基数反馈请参考:http://www.oracle.com/technetwork/issue-archive/2010/10-sep/o50asktom-165477.html

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

Dylan    Presents.

时间: 2024-08-19 12:15:52

如何获得正确的基数估计值的相关文章

KV型内存数据库Redis

Redis是开源的高性能内存Key-Value数据库, 可以提供事务和持久化支持, 并提供了TTL(time to life)服务. Redis采用单线程数据操作+非阻塞IO的模型,非阻塞IO提供了较高的IO性能,单线程操作保证了单条指令的原子性. Redis使用简单灵活性能优异,常被用作缓存,分布式锁或者消息队列. 非特殊说明, 本文以Redis 3.0为标准进行介绍. Redis数据结构 string SET GET MGET MSET MSETNX TYPE INCR,DECR INCRB

大数据计算:如何仅用1.5KB内存为十亿对象计数

在Clearspring,我们从事统计数据.统计一组不同元素且数量很大的数据集时,是一个挑战. 为了更好地理解已经明确基数的大数据集的挑战,我们假设你的日志文件包含16个字符的ID,并且你想统计不同ID的数量.例如: 4f67bfc603106cb2 这16个字符需要用128位来表示.6万5千个ID将需要1MB的空间.我们每天收到30多亿条事件记录,每条记录都有一个ID.这些ID需要3840亿位或45GB的存储.而这仅仅是ID字段需要的空间.我们采取一种简单的方法获取日常事件记录中以ID为基数的

EF提高性能

实体框架 5 性能注意事项 作者:David Obando.Eric Dettinger 等 发布时间:2012 年 4 月 1.简介 对象关系映射框架是一种在面向对象的应用程序中提供数据访问抽象的便捷方式.对于 .NET 应用程序,Microsoft 推荐的 O/RM 是实体框架.但任何抽象都要考虑性能. 本白皮书旨在介绍在使用实体框架开发应用程序时的性能注意事项,使开发人员了解能够影响性能的实体框架内部算法,以及提供有关进行调查及在使用实体框架的应用程序中提高性能的提示.网络上有大量很好的有

EntityFrame Work 5 性能注意事项(转自MSDN)

1.简介 对象关系映射框架是一种在面向对象的应用程序中提供数据访问抽象的便捷方式.对于 .NET 应用程序,Microsoft 推荐的 O/RM 是实体框架.但任何抽象都要考虑性能. 本白皮书旨在介绍在使用实体框架开发应用程序时的性能注意事项,使开发人员了解能够影响性能的实体框架内部算法,以及提供有关进行调查及在使用实体框架的应用程序中提高性能的提示.网络上有大量很好的有关性能的主题,我们还尽可能地指出这些资源的链接. 性能是一个很微妙的主题.对于使用实体框架的应用程序,可将本白皮书作为资源来帮

动态采样---DYNAMIC_SAMPLING 基于tom文章的翻译

我们的技术人员动态地采样,考虑使用情况,并设置水平. 我的问题与动态抽样有关. 它真正做什么,什么时候考虑使用它,以及可以设置的所有不同级别的含义是什么? 动态采样首先在Oracle9 i数据库版本2中可用.基于成本的优化器(CBO)可以在硬解析过程中对查询引用的表进行采样,以确定未分析段的更好的默认统计信息,并验证其"猜测".此抽样仅在硬解析时才会进行,并用于动态生成优化器使用的更好的统计信息,因此名称为动态抽样. 优化器使用各种输入来制定计划. 它使用表上定义的任何和所有约束; 系

我如何调优SQL Server查询

我是个懒人,我只想干尽可能少的活.当我干活的时候我不想太多.是,你没看错,这看起来很糟糕,作为一个DBA这很不合格.但在今天的文章里,我想给你展示下,当你想对特定查询创建索引设计时,你如何把你的工作和思考过程传达给查询优化器.听起来很有意思?嗯,那就进入我的索引调优世界吧! 有问题的查询 我们来看下列查询: 1 DECLARE @i INT = 999 2 SELECT 3 SalesOrderID, 4 SalesOrderDetailID, 5 CarrierTrackingNumber,

实体框架 5 性能注意事项

1.简介 对象关系映射框架是一种在面向对象的应用程序中提供数据访问抽象的便捷方式.对于 .NET 应用程序,Microsoft 推荐的 O/RM 是实体框架.但任何抽象都要考虑性能. 本白皮书旨在介绍在使用实体框架开发应用程序时的性能注意事项,使开发人员了解能够影响性能的实体框架内部算法,以及提供有关进行调查及在使用实体框架的应用程序中提高性能的提示.网络上有大量很好的有关性能的主题,我们还尽可能地指出这些资源的链接. 性能是一个很微妙的主题.对于使用实体框架的应用程序,可将本白皮书作为资源来帮

Hyper LogLog介绍

大数据计算:如何仅用1.5KB内存为十亿对象计数 - Hyper LogLog 算法 Big Data Counting: How To Count A Billion Distinct Objects Using Only 1.5K This is a guest post by Matt Abrams (@abramsm), from Clearspring, discussing how they are able to accurately estimate the cardinalit

Redis学习一(基础入门).

一.前言 Redis是一个开源的使用ANSI C语言编写.遵守BSD协议.支持网络.可基于内存亦可持久化的日志型.key-Value 的数据库.并提供多种语言的API. 通常,Redis 将数据存储于内存中,或被配置为使用虚拟内存.通过两种方式可以实现数据持久化:使用截图的方式,将内存中的数据不断写入磁盘:或使用类似 MySQL 的日志方式,记录每次更新的日志.前者性能较高,但是可能会引起一定程度的数据丢失:后者相反. 解释了为什么选用6379作为默认端口,因为6379在手机按键上MERZ对应的