楼主: newkid

[翻译]Jonathan Lewis 关于直方图的系列文章(33楼更新至第三篇)

[复制链接]
论坛徽章:
27
ITPUB官方微博粉丝徽章
日期:2011-08-17 10:35:36托尼托尼·乔巴
日期:2017-10-25 16:45:57秀才
日期:2017-04-05 13:18:06秀才
日期:2017-03-02 10:35:322016猴年福章
日期:2016-02-23 09:58:342016猴年福章
日期:2016-02-18 09:31:302015年新春福章
日期:2015-03-06 11:57:312014年新春福章
日期:2014-02-18 16:42:022013年新春福章
日期:2013-02-25 14:51:24ITPUB 11周年纪念徽章
日期:2012-10-09 18:07:31
31#
发表于 2013-10-17 15:46 | 只看该作者
newkid 发表于 2013-10-16 10:21
哪里看不懂提出来我们共同探讨。

对虚拟列这个概念很陌生,从来没接触过,只听说过

使用道具 举报

回复
论坛徽章:
520
奥运会纪念徽章:垒球
日期:2008-09-15 01:28:12生肖徽章2007版:鸡
日期:2008-11-17 23:40:58生肖徽章2007版:马
日期:2008-11-18 05:09:48数据库板块每日发贴之星
日期:2008-11-29 01:01:02数据库板块每日发贴之星
日期:2008-12-05 01:01:03生肖徽章2007版:虎
日期:2008-12-10 07:47:462009新春纪念徽章
日期:2009-01-04 14:52:28数据库板块每日发贴之星
日期:2009-02-08 01:01:03生肖徽章2007版:蛇
日期:2009-03-09 22:18:532009日食纪念
日期:2009-07-22 09:30:00
32#
 楼主| 发表于 2013-10-17 23:07 | 只看该作者
gyhgood 发表于 2013-10-17 15:46
对虚拟列这个概念很陌生,从来没接触过,只听说过

虚拟列都出来好多年了,你可以理解为VIEW里面那些计算列,但有很多好处,可以收集STATS, 可以建约束,可以建索引(这个跟FBI差不多),可以做分区键,......

使用道具 举报

回复
论坛徽章:
520
奥运会纪念徽章:垒球
日期:2008-09-15 01:28:12生肖徽章2007版:鸡
日期:2008-11-17 23:40:58生肖徽章2007版:马
日期:2008-11-18 05:09:48数据库板块每日发贴之星
日期:2008-11-29 01:01:02数据库板块每日发贴之星
日期:2008-12-05 01:01:03生肖徽章2007版:虎
日期:2008-12-10 07:47:462009新春纪念徽章
日期:2009-01-04 14:52:28数据库板块每日发贴之星
日期:2009-02-08 01:01:03生肖徽章2007版:蛇
日期:2009-03-09 22:18:532009日食纪念
日期:2009-07-22 09:30:00
33#
 楼主| 发表于 2013-10-23 03:29 | 只看该作者

直方图系列文章(三): 何时?

Jonathan Lewis 发表于2013年10月16号
原文链接:
http://allthingsoracle.com/histograms-part-3-when/

在第二篇中我们查看了Oracle收集和使用等高直方图的方法,并提出了一个问题,就是直方图对于取样者中或者数据中的细微修改是如何的敏感。在文章的结尾我们建议你可以自己产生“近似”的,然而可能已经“足够好”的频度直方图数据来帮助优化器做出正确的事情。

然后,在第三篇中,我们将会查看几个例子,这时允许Oracle自己做主可能是合理的且充足的产品环境策略。作为一个普遍的指导原则,我发现仅仅当你使用等高直方图来“拿掉”很少的一部分频度很高的值时,才是合理且安全的——实际上这时就是允许优化器来为剩下的“感兴趣”的数据(即未拿掉的那些非频繁出现的值)做优化。然而,有那么几种设计错误,在那些情况下你可以使用直方图作为一种止损的机制。

我一直想针对如何将直方图的收集包含到完整的统计信息收集策略中发表一些看法,但这篇文章比我预想的要长,所以我将关于统计收集的实现方法的想法推迟,以后再说。

数据库不可知论的应用系统

有那么一些原生态的应用,它们写出来就是要能在“任何”数据库上运行,它们也就试图将对任何数据库功能的依赖性最小化。这样的后果就是,“在任何数据库上运行”就意味着“在任何数据库上拙劣地运行”。

从纯粹的数据角度来说,不可知论的两个最普遍的特点是:

我们不用NULL——我们用一个特殊的(极端)的值。
我们不用日期数据类型。

这在Oracle的优化器上可能带来灾难性的副作用。

我们不用NULL

这里有个脚本,它创建了一个简单的数据集来证明这个论点。这份数据代表5年的数据(从2008年1月1日开始),每天100行,每1000行就有一行被设为“伪NULL”值4000年12月31日。创建数据之后我收集了统计信息,但没有生成直方图,也即使用了参数 method_opt => 'for all columns size 1'

create table t1
as
with generator as (
     select     --+ materialize
          rownum     id
     from all_objects
     where rownum <= 2000
)
select
     /*+ ordered use_nl(v2) */
     decode(
          mod(rownum - 1,1000),
               0,to_date('31-Dec-4000'),
                 to_date('01-Jan-2008') + trunc((rownum - 1)/100)
     )    date_closed
from
     generator  v1,
     generator  v2
where
     rownum <= 1827 * 100
;

我不担心索引或者优化的存取路径的细节,现在我感兴趣的只是优化器的基数计算,它离正确答案有多接近。基于这个SQL, 你能看出一个“2010年所有数据”的查询应该返回36,500行——所以我们来写一个查询索取这些数据,然后检查结果的执行计划中的行数预测:
set autotrace traceonly explain

select
     *
from t1
where date_closed between to_date('01-Jan-2010','dd-mon-yyyy')
               and     to_date('31-Dec-2010','dd-mon-yyyy')
;

----------------------------------------------------------
| Id  | Operation         | Name | Rows  | Bytes | Cost  |
---------------------------------------------------------
|   0 | SELECT STATEMENT  |      |   291 |  2328 |    52 |
|*  1 |  TABLE ACCESS FULL| T1   |   291 |  2328 |    52 |
----------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------
   1 - filter("DATE_CLOSED">=TO_DATE(' 2010-01-01 00:00:00',
              'syyyy-mm-dd hh24:mi:ss') AND "DATE_CLOSED"<=TO_DATE(' 2010-12-31
              00:00:00', 'syyyy-mm-dd hh24:mi:ss'))

优化器预测的行数是291, 看起来偏差系数有125倍; 在大部分情况下,这么大的错误可能导致执行计划的错误选择。

为什么会这样?基本的优化器模型假设你的数据在低值和高值之间是均匀分布的,所以Oracle的估算是(忽略几个边界舍入的细节):

“表中非空值的行数” * “你需要的区间” / “最高值 – 最低值”

在我的例子中是这样(近似地):
182700 * 365 / 727928 + 200

Oracle以为我要从2,000年的数据中取出一年,而不是5年中取一年(带有两个100行的修正,这是考虑到有界区间的端点值) 。

在这时候我可以简单地写一小段PL/SQL,它调用dbms_stats.set_column_stats()来将这个列的最高值修改为更接近“合适”的最高值。如果偏僻的数据量(指年份为4000的“伪NULL”数据)很小,这就是一个合理的策略,你只需要记住,对于“伪NULL”的查询将会超出已知的区间很远,以至于优化器很可能为那个值给出行数为1的基数估算,不管你实际上有多少数据存在。

然而,不同于操纵统计信息的方法,我将会在此列上创建一个直方图。典型情况下我的目标是254桶(11g最大值),但是作为高效性的演示,我实际上会展示仅有11个桶(每两年一个,还有一个空的)直方图的效果——这是改变过后的计划:
----------------------------------------------------------
| Id  | Operation         | Name | Rows  | Bytes | Cost  |
----------------------------------------------------------
|   0 | SELECT STATEMENT  |      | 36420 |   284K|    52 |
|*  1 |  TABLE ACCESS FULL| T1   | 36420 |   284K|    52 |
----------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

1 - filter("DATE_CLOSED">=TO_DATE(' 2010-01-01 00:00:00',
           'syyyy-mm-dd hh24:mi:ss') AND "DATE_CLOSED"<=TO_DATE(' 2010-12-31
           00:00:00', 'syyyy-mm-dd hh24:mi:ss'))

优化器的估算值36,420几乎是完美的。当对数据进行取样时,Oracle发现从2012年7月到4000年12月的数据只有十一分之一,而十一分之十的数据在2008年1月到2012年7月之间均匀分布,所以直方图给了它“理解”数据在我要求的日期区间内真正的面貌所需的信息(在7月20日之后的日期就不那么准确了,这就是我为什么实际上想要使用254个桶)。

注意:你看到的关于直方图的大部分文章描述了它们是如何帮助优化器来处理数据中的“尖峰”或者“频繁值”。其实,对数据中的任何模式,只要不是单调,平直,连续和统一的,直方图都会有帮助。在这个例子中,直方图告诉优化器,在数据的分布中有一个大缺口。

这就是视图user_tab_histograms关于这个数据集的内容(对于日期型,存储的值是儒略日格式):

ENDPOINT_NUMBER ENDPOINT_VALUE EPV_CONVERTED
--------------- -------------- -------------
              0        2454467   01-Jan-2008
              1        2454633   15-Jun-2008
              2        2454799   28-Nov-2008
              3        2454965   13-May-2009
              4        2455132   27-Oct-2009
              5        2455298   11-Apr-2010
              6        2455464   24-Sep-2010
              7        2455630   09-Mar-2011
              8        2455797   23-Aug-2011
              9        2455963   05-Feb-2012
             10        2456129   20-Jul-2012
             11        3182395   31-Dec-4000

我们不用日期型

有些应用将日期存为字符串,要么是“YYYYMMDD”格式,要么是等价的数值型。这允许日期可读,同时保留正确的排序顺序。这是个样本数据集——还是从2008年1月运行到2012年12月,但这次每天只有一行,也没有“伪NULL”值:

create table t1
as
select
     d1,
     to_number(to_char(d1,'yyyymmdd'))    n1,
     to_char(d1,'yyyymmdd')               v1
from (
     select
          to_date('31-Dec-2007') + rownum d1
     from all_objects
     where
          rownum <= 1827
     )
;

我将会写出差不多是我所能写出的最脏乱的查询来证明一个论点——我将会先在任何一列都没有直方图的情况下测试它,然后在直方图下测试。查询只是想问:“在2011年12月30日和2012年1月5日之间有多少天?”见多识广的DBA会算出答案是7(也可能是5,或6, 取决于他们如何解释这个“之间”)。优化器又会怎么想?

这就是我查询日期型列得到的答案:

----------------------------------------------------------
| Id  | Operation         | Name | Rows  | Bytes | Cost  |
----------------------------------------------------------
|   0 | SELECT STATEMENT  |      |     8 |   184 |     3 |
|*  1 |  TABLE ACCESS FULL| T1   |     8 |   184 |     3 |
----------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------
1 - filter("D1">=TO_DATE(' 2011-12-30 00:00:00', 'syyyy-mm-dd
           hh24:mi:ss') AND "D1"<=TO_DATE(' 2012-01-05 00:00:00', 'syyyy-mm-dd
           hh24:mi:ss'))

它相当地接近——错误是由于优化器对区间的处理细节,它在选择性上加了1/num_distinct, 这是考虑到有界区间的端点值。

这是等价的VARCHAR2()类型值的查询计划(除非你是在很老的Oracle版本上运行):

----------------------------------------------------------
| Id  | Operation         | Name | Rows  | Bytes | Cost  |
----------------------------------------------------------
|   0 | SELECT STATEMENT  |      |   396 |  9108 |     3 |
|*  1 |  TABLE ACCESS FULL| T1   |   396 |  9108 |     3 |
----------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   1 - filter("V1">='20111230' AND "V1"<='20120105')

答案偏差了79倍——这是不可接受的,并且很可能会生成一些劣质的执行计划。顺便说一下,如果你在数值型的列上尝试同样的查询,你会得到同样的结果——我想有一段“特殊的”优化器代码,在处理看起来像数字的字符串数据时,它使用了数值型的算法(也即当高,低,谓词的值看起来全部都像数值)。

错误是怎么来的呢?Oracle可以做日期算术,所以它能够准确地算出我们需要的区间:
2012年1月5日 - 2011年12月30日 = 6 这就是将计算引入正轨的数字。另一方面,如果你查看的是8位数字的数值型列:20120105 – 20111230 = 8,875 你就会开始怀疑,一些奇怪的基数可能会出现。

如果你将日期想像为一系列的数字,你得到的是(大致)30个连续的数字,然后跳过大约70个数字到下一个月,这样重复11次,然后又有一个大约9000的间隙进入下一年。如果优化器以为你的数据在20080131 和 20121231之间均匀分布,它的基数预测将十分散乱。

那么就创建一个直方图——这次我选了120个桶,为了捕获每个月的细节我们确实需要许多的桶(在产品系统上我会用254)——然后来看看效果:

----------------------------------------------------------
| Id  | Operation         | Name | Rows  | Bytes | Cost  |
----------------------------------------------------------
|   0 | SELECT STATEMENT  |      |    15 |   345 |     3 |
|*  1 |  TABLE ACCESS FULL| T1   |    15 |   345 |     3 |
----------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------
   1 - filter("V1">='20111230' AND "V1"<='20120105')

结果好多了——还不完美,但是可能足够精确让你得到所需要的计划。再说一次,你可能会注意到,我们并没有使用直方图来突出数据中的“尖峰”——每个单独的值都出现一样多的次数——我们利用直方图向优化器指出在数据中从低值到高值之间的那些间隙。

关于这个例子还有一点必须阐明:这个基数问题的理想解决方案是创建一个虚拟列(在早期版本的Oracle中可能是一个基于函数的索引), 它将存储的数据转换为正确的类型,例如:

alter table t1
add (
     d1_virtual generated always as (to_date(v1,'yyyymmdd')) virtual
)
;
begin
     dbms_stats.gather_table_stats(
          user,
          't1',
          method_opt=>'for columns d1_virtual size 1'
     );
end;
/

只要你能够修改SQL来引用虚拟列(或者匹配基于函数的索引),你现在得到了一个正确的数据类型的列,还有正确的统计信息,使得优化器有最佳的机会可以计算出正确的基数。

结论

在缺乏直方图的情况下,优化器假设你的数据是从低值到高值平均分配的,中间没有间隙,也没有尖峰;如果你把你的数据画成图,看到的是一条连续平直的线,那么这就是优化器对你的数据的映像。如果你的数据的图显示出很大的间隙,或者是一种大幅度偏离了直线的图案,那么直方图就可能有用,尽管如同我们在第二篇中看到的,它们可能对细小的修改很敏感,并且在你的执行计划中引入了不稳定因素。

有那么几种情形,直方图可以用来解决你应用的拙劣设计——使用极端的值来取代NULL,或者将数据存为错误的类型(最显著的例子是将日期保存为字符串或者数值型)。在这些情况下,理想的策略是纠正设计的缺陷,但是修改数据库结构或者应用程序经常是办不到的事,所以直方图可能会帮助优化器处理这些设计带来的副作用。




使用道具 举报

回复
论坛徽章:
2
劳斯莱斯
日期:2013-11-14 21:47:372016猴年福章
日期:2016-02-23 09:58:34
34#
发表于 2013-11-14 22:16 | 只看该作者
看的不是太明白,演示的例子不完整

使用道具 举报

回复
论坛徽章:
520
奥运会纪念徽章:垒球
日期:2008-09-15 01:28:12生肖徽章2007版:鸡
日期:2008-11-17 23:40:58生肖徽章2007版:马
日期:2008-11-18 05:09:48数据库板块每日发贴之星
日期:2008-11-29 01:01:02数据库板块每日发贴之星
日期:2008-12-05 01:01:03生肖徽章2007版:虎
日期:2008-12-10 07:47:462009新春纪念徽章
日期:2009-01-04 14:52:28数据库板块每日发贴之星
日期:2009-02-08 01:01:03生肖徽章2007版:蛇
日期:2009-03-09 22:18:532009日食纪念
日期:2009-07-22 09:30:00
35#
 楼主| 发表于 2013-11-15 03:36 | 只看该作者
有些地方我翻译完也觉得很拗口,但还是尽量尊重原文。如果你对直方图原来就有些理解,还是不难懂的。哪里不明白具体指出来。

使用道具 举报

回复
论坛徽章:
26
2010年世界杯参赛球队:智利
日期:2010-07-03 17:16:26比亚迪
日期:2014-01-16 17:12:41宝马
日期:2014-01-24 10:32:252014年新春福章
日期:2014-02-18 16:44:08马上有对象
日期:2014-02-18 16:44:08马上有对象
日期:2014-03-05 21:30:32马上有车
日期:2014-03-11 16:46:45优秀写手
日期:2014-03-25 05:59:50马上加薪
日期:2014-03-26 16:46:30问答徽章
日期:2014-05-09 16:40:36
36#
发表于 2013-11-16 17:23 | 只看该作者
mark,回去看。。。

使用道具 举报

回复
论坛徽章:
314
行业板块每日发贴之星
日期:2012-07-12 18:47:29双黄蛋
日期:2011-08-12 17:31:04咸鸭蛋
日期:2011-08-18 15:13:51迷宫蛋
日期:2011-08-18 16:58:25紫蛋头
日期:2011-08-31 10:57:28ITPUB十周年纪念徽章
日期:2011-09-27 16:30:47蜘蛛蛋
日期:2011-10-20 15:51:25迷宫蛋
日期:2011-10-29 11:12:59ITPUB十周年纪念徽章
日期:2011-11-01 16:19:41鲜花蛋
日期:2011-11-09 20:33:30
37#
发表于 2013-12-10 21:06 | 只看该作者
newkid 发表于 2013-9-14 10:53
文中讲得很清楚,建索引最好用函数索引,收集直方图要讲究时机。至于绑定变量,如果一定要用,比如plsql中的 ...

我对作者的说法不以为然,

1  作者的案例, 其实, 我要的话, 我最可能考虑列表分区,
2  对于作者列的这几条直方图的危害:

■它们不能和绑定变量很好地相容;
■计算的代价很昂贵;
■在取样的时候它们可能很不稳定;
■你必须在恰当的时机进行收集。

2.1  我认为, 随着技术的发展, 和绑定变量不好相容的问题, 会逐步解决, 就如11G的自适应共享游标
和12C的自适应执行计划 一样.

2.2  我不在乎, 我有足够的资源采集分析统计, 另外, 我可能针对不同的表, 定义不同的采集时间和样本.
2.3  不认可, 小部分不稳定, 不影响整体.
2.4  不认可.

我觉得, 直方图是个好东西, 只是254桶数太少, 应该不加以限制, 而是根据字段的唯一个数和采集比例间
做个平衡, 即采样够准确, 又尽量少占资源, 希望12C里对直方图有锁改进.

使用道具 举报

回复
论坛徽章:
2
ITPUB 11周年纪念徽章
日期:2012-10-09 18:06:20ITPUB社区12周年站庆徽章
日期:2013-10-08 15:00:34
38#
发表于 2013-12-10 22:09 | 只看该作者
好文章,感谢楼主

使用道具 举报

回复
论坛徽章:
520
奥运会纪念徽章:垒球
日期:2008-09-15 01:28:12生肖徽章2007版:鸡
日期:2008-11-17 23:40:58生肖徽章2007版:马
日期:2008-11-18 05:09:48数据库板块每日发贴之星
日期:2008-11-29 01:01:02数据库板块每日发贴之星
日期:2008-12-05 01:01:03生肖徽章2007版:虎
日期:2008-12-10 07:47:462009新春纪念徽章
日期:2009-01-04 14:52:28数据库板块每日发贴之星
日期:2009-02-08 01:01:03生肖徽章2007版:蛇
日期:2009-03-09 22:18:532009日食纪念
日期:2009-07-22 09:30:00
39#
 楼主| 发表于 2013-12-10 22:54 | 只看该作者
ZALBB 发表于 2013-12-10 21:06
我对作者的说法不以为然,

1  作者的案例, 其实, 我要的话, 我最可能考虑列表分区,

列表分区比起虚拟列+索引的方法好在哪里?可能无需维护索引,但是每次修改状态都会导致行的物理移动。
2.1 确实ORACLE一直在改进硬解析这块,一个SQL只对应一种计划是很有问题的。
2.2 即使资源足够也不能浪费,取样有2.3说的问题,"定义不同的采集时间"是为了避开负载高峰呢,还是你所不认同的2.4?
2.3 倾斜数据肯定是不均匀分布的,因此取样得到的样本肯定是无法精准地反映它们的特征。有时候你恰恰就栽在这小部分的不稳定上面了。
2.4为什么不认可?作者举出的例子说明了有些数据只在某特定时间段出现,你不在这时候采集就错过了。

使用道具 举报

回复
论坛徽章:
520
奥运会纪念徽章:垒球
日期:2008-09-15 01:28:12生肖徽章2007版:鸡
日期:2008-11-17 23:40:58生肖徽章2007版:马
日期:2008-11-18 05:09:48数据库板块每日发贴之星
日期:2008-11-29 01:01:02数据库板块每日发贴之星
日期:2008-12-05 01:01:03生肖徽章2007版:虎
日期:2008-12-10 07:47:462009新春纪念徽章
日期:2009-01-04 14:52:28数据库板块每日发贴之星
日期:2009-02-08 01:01:03生肖徽章2007版:蛇
日期:2009-03-09 22:18:532009日食纪念
日期:2009-07-22 09:30:00
40#
 楼主| 发表于 2013-12-11 04:59 | 只看该作者
另外,文章中确实提到了12C对直方图的改进,比如增加到2000个桶。以前就有人向我们公司推荐他们写的工具,可以把11G之下的直方图黑掉,用自己写的采集程序往字典里填写,桶数可以超过一千,我当时觉得这有点冒险,不知道会对CBO造成什么副作用,没有建议采用。

使用道具 举报

回复

您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

TOP技术积分榜 社区积分榜 徽章 团队 统计 知识索引树 积分竞拍 文本模式 帮助
  ITPUB首页 | ITPUB论坛 | 数据库技术 | 企业信息化 | 开发技术 | 微软技术 | 软件工程与项目管理 | IBM技术园地 | 行业纵向讨论 | IT招聘 | IT文档
  ChinaUnix | ChinaUnix博客 | ChinaUnix论坛
CopyRight 1999-2011 itpub.net All Right Reserved. 北京盛拓优讯信息技术有限公司版权所有 联系我们 未成年人举报专区 
京ICP备16024965号-8  北京市公安局海淀分局网监中心备案编号:11010802021510 广播电视节目制作经营许可证:编号(京)字第1149号
  
快速回复 返回顶部 返回列表