a little experience on hacking clustering_factor

2010 March 9

有些时候,系统中会出现部分SQL语句, 由于数据分布的原因, 无法正常使用索引,,基本的情形在前面翻译的Jonathan Lewis的文章中已经有所涉及.

下面是一个简单的Hack clustering_factor的方法(注意: 尽量不要使用,只有在正常分析表无法解决问题的时候, 又很难通过使用SPM或者SQL Profile的时候才可以考虑一试).
这样做会有以下几个弊端:

  • 1. 这么做的结果可能导致后续的维护有困难,,因为你的后任可能根本不知道你这么处理了.
  • 2. hack 系统的信息, 不是一种好的处理方式..:-)

下面是一个基本的处理过程

  • 1. 对于当前的SQL运行EXPLAIN PLAN, 并检查cost的大小
  • explain plan for
    select column_list
    from table_name m
    where m.column_name = ‘Y’
    and m.column_name2 in ( :1 , :2 , :3 , :4 , :5 , :6 , :7 , :8 , :9 );

  • 2. 添加使用index的提示,,运行EXPLAIN PLAN, 并检查cost的大小
  • explain plan for
    select /*+ index(m)*/column_list
    from table_name m
    where m.column_name = ‘Y’
    and m.column_name2 in ( :1 , :2 , :3 , :4 , :5 , :6 , :7 , :8 , :9 );

  • 3. 检查index segment的blocks以及当前的clustering_factor
  • select clustering_factor from dba_indexes where index_name = ‘INDEX_NAME’;
    select blocks from dba_segments where segment_name = ‘INDEX_NAME’;

  • 4. 设置clustering_factor
  • DBMS_STATS.SET_INDEX_STATS (ownname =>’OWNER’,indname => ‘INDEX_NAME’,clstfct => 100);

  • 5. 重做第1,2两步确认是否已经生效
  • 日志文件切换

    2010 March 5

    本文主要翻译自Jonathan Lewis的Blog Entry Log File Switch, 最后关于checkpoint部分,我添加了部分我自己的注释(以斜体字显示).

    日志文件切换

    几天前,我发布一个简单的查询语句,它通过查询v$log_history视图展示日志文件切换之间的时间间隔.如果你运行这个脚本,并且认为你的系统的日志文件切换频率不合适,那么你该如何处理呢?
    如果时间间隔太短(由于日志文件频繁切换引发的checkpoint动作可能会导致DBWR进程过于活跃),可以通过新增新的更大的日志文件并删除旧的日志文件来解决.
    如果切换的时间间隔太久,或者是时间间隔波动太大,而你希望使日志切换更加有规律,那么可以使用参数archive_lag_target(在Oracle 9i中引入)来为日志文件切换设置一个超时值(单位为秒).如果日志文件没有在指定的时间间隔内发生切换,那么系统会强制其切换日志,并且归档对应的已经使用的在线重做日志.
    在此之前,人们一般使用类似于cron或者dbms_job一类的工具来实现(通过执行一条”alter system switch logfile“命令).从Oracle 9i开始,利用这个参数来实现就简单多了.

    附录

    • Instance Recovery Checkpoint
    • 实例恢复触发的检查点

    • Media Recovery Checkpoint
    • 介质恢复触发的检查点

    • Thread Checkpoint
    • 单个instance关闭以及log file switch触发的检查点

    • Interval Checkpoint
    • log_checkpoint_timeout参数超时触发的检查点

    • Tablespace Checkpoint
    • 表空间online/offline/read only/read write操作对应的检查点

    • PQ Tablespace Checkpoint
    • 不清楚具体原因

    • Close Database Checkpoint
    • Incremental Checkpoint
    • 常规的检查点动作

    • Local Database Checkpoint
    • 对应于alter system checkpoint

    • Global Database Checkpoint
    • 对应于alter system checkpoint all命令的检查点.

    • Object Reuse Checkpoint
    • 对应于truncate table操作的检查点

    • Object Checkpoint
    • 对应于drop table的检查点操作

    Oracle支持多种不同类型的checkpoint,这个列表会随着Oracle版本的不同而有所不同.Oracle 10.2.0.3的对应的checkpoint看似有如下这些:
    在这些中间,我认为日志文件切换时引发的checkpoint可能是ThreadCheckpoint.

    推荐几个ronald bradford关于mysql的ppt

    2010 March 3
    tags: ,
    by jametong

    推荐几个ronald bradford发布在SlideShare上的ppt.

    每个(NoSQL)项目都需要考虑的6个问题

    2010 March 2
    tags:
    by jametong

    正文主要来自MynoSQL网站.

    ILoggable 博客提出了6非常有趣的问题, 这些问题对于决定使用新的存储解决方案(是否NoSQL并不重要)的项目都有一定的参考意义.

    • 1. 可以诊断其中的性能问题吗?
    • 每一个RDBMS都一定的剖析工具以及相关的方法来诊断其性能问题..
      例如: Oracle提供了Wait Interface以及基于10046/10053的诊断事件来分析Oracle的性能问题.

    • 2. 基于非主键查找的并发性如何?
    • 3. 有哪些工具可以用来帮助恢复损坏的数据文件?
    • 在任何RDBMS系统中,都有工具可用来帮助恢复损坏的数据文件或者索引文件.

    • 4. 有什么样的备份策略?
    • 大部分数据库都有非阻塞模式的数据备份方式. 几乎所有RDBMS都支持复制功能. 都有有效的机制来做备份.
      例如: Oracle支持适用Rman做备份, 适用DataGuard做数据库物理/逻辑复制.

    • 5. 如何确认备份是有效的?
    • 是否有工具或者其他手段帮助你确认复制是有效的并且是最新的, 而且并没有将损坏的数据复制进去.

    • 6. 系统管理员是否认同这一点?
    • 如果你认为你不需要关系所有这些问题, 可能是有其他人已经帮你做好这些事情了.

      你期望的NoSQL方案有如上这些问题的答案吗?

    不平衡的索引?

    2010 March 1

    本文翻译自Jonathan Lewis早年写在dbazine上的文章unbalanced indexes? 本文的word版本可以到此处下载

    不平衡的索引?
    by Jonathan Lewis

    网络上有多篇介绍Oracle索引实现机制的文章,都提及需要经常重建索引.在这些文章中的某处,总是会出现这样一段简短的描述,索引会如何变的不平衡,以及可能导致的后果.很不幸,它们好像忽视了这样一个事实,Oracle使用的B-tree机制是一种”平衡B-tree”索引,也就是说,索引无法变得不平衡.

    “平衡”到底意味着什么?

    既然Oracle的索引使用的是平衡B-tree,为什么还有如此多的人相信他们的索引会变得不平衡呢?
    另外,平衡B-tree到底又是什么呢?
    第二个问题的答案可能能够帮助我们得到第一个问题的答案.
    从技术角度讲,平衡B-tree的显要特性是,在任意时间点,任何叶子节点(leaf block)到根节点(root block)的距离都相等,平衡是指从顶部到底部的平衡.
    就Oracle来说,执行一个treedump命令就可以很容易发现这一点,如图-1所示:

    select	object_id
    from	user_objects
    where	object_type = 'INDEX'
    and	object_name = 'T1_IDX1'
    -- and 	subobject_name = . . .
    ;
    alter session set events '
    'immediate trace name treedump level N';
    

    图-1: 一次索引树转储涉及到的步骤

    首先,需要找到你想要转储(dump)索引或者索引分区(index partition)的object_id;接着,将object_id作为level的参数来调用treedump事件. 如果检查这个索引树(tree dump)转储生成的跟踪文件,你将发现类似于图-2中所示的结果.

    branch: 0x14001aa 20971946 (.. level 2)
      branch: 0x14003ef 20972527 (.. level 1)
        leaf: 0x14001ab 20971947 (..)
        leaf: 0x14001ac 20971948 (..)
    

    图-2: 从索引树转储结果中提取出的一段内容

    这个跟踪文件以递归降序的方式展示索引的分支块(branch block,根块只是分支块的一个特例)与叶子块.注意,转储内容的第一个块(根块)记录了一个高度(level),并且它下面的每一个分支块也都记录了一个高度,但是叶子块没有记录高度.
    根块的高度就是索引的blevel(执行analyze index命令之后会记录在视图dba_indexes中).索引的高度(height,执行命令validate index后会记录在视图index_stats中)就是blevel + 1.
    每一个叶子块到根块的距离就正好有这么多步.索引总是平衡的.
    那么为什么有那么多人相信Oracle会允许索引变得不平衡呢?

    我应负的责任

    此时,我必须承认我有罪,仅仅在一年前(2002年5月),我也重复了一个关于块分裂(index block split)的众所周知但是却完全错误的描述.虽然我知悉(在我的书(2000年12月,Practical Oracle 8i)中对此做了说明) treedump的细节,但我还是这样做了.
    我猜想,这种错误的观念最初产生于Oracle5(很多年以前)手册的说明,其大意是,因为”没有一个叶子块到根块的距离比任何其他叶子块到根块的距离远.”, 所以Oracle索引是平衡的.将这个表述与一个过于简化的块分裂的图结合到一起,瞧,一个几乎牢不可破的神话就诞生了.
    图-3和图-4描绘了一个非常常见,但是完全错误的关于叶子块如何分裂的概念.

    图-3: 叶子块将要分裂前的索引结构

    图-4 这不是叶子块分裂发生的方式

    有这样一个流传广泛的说法,叶子块分裂到两个全新的块,这两个块分享它的所有数据;接着, Oracle在原来的叶子块的位置插入一个新的分支块来持有指向这两个块的指针.因此,在这样一个错误的视角下,这个索引的右边就会比左边更深.(经常会有人说,创建在基于序列的字段上的索引会带来最大的问题,因为,从这个理论来推导,最右边的叶子块到根块的距离会越来越远,没分裂一次,就会降低一层.)
    事实上,这个工作Oracle做得更加精致前瞻并有效得多. 图-5所示是一个复杂的叶子块分裂的结果.

    图-5: 一次递归分裂后的索引结构图
    由于这个叶子块分裂成了两个块,Oracle会尝试往当前指向这个叶子块的分支块中插入一个额外的指针.
    但是,如果这个分支块也满了,Oracle会继续将这个分支块也分裂成两个块,并且在两个分支块之间分配现存块里的指向叶子块的指针,并且(递归地) 在这个分支块的上一级的分支块中插入一个指向这个新的分支块的新的分支指针.
    如果在这个过程中,Oracle抵达了根块,然而这个根块也满了,那么根块也必须分裂.在这种情况下,Oracle将创建一个新的根块来持有这两个分支指针.(事实上,Oracle处理根块分裂的方式与处理普通的分支块的分裂有一点细微的差异,以确保无论根块上发生了多少次的分裂,总是可以在物理段的同一个位置找到根块.)
    注意,这个递归的分裂操作这样沿着索引树不断攀升意味着,无论何时,索引总能保持平衡.

    为什么这个神话如此牢固?

    为什么这个关于不平衡的索引的神话能够长盛不衰,是否存在一些原因呢?我这答案是确实存在一些原因.
    要切记,当我们讨论B-tree的时候,单词”balanced”的定义有非常严格的含义.然而,这个单词还有一种完全不同的解释.
    例如,你将如何描述图-6中的这个索引,其中根块指向六个叶子块,但是其中一个叶子块是空的,有三个块几乎是空的,还有两个块塞的很满.(注意,从根块到叶子块的这些额外的说明是为了强调索引填充分布的多么不均匀;实际上,根块到每个叶子块都只有一个指针.)

    图-6 “不平衡”的一种非技术理解

    看到这种图案的索引,一个”人”的真实反应都会人为它”不平衡”.很明显,索引的右手边要比左手边”重”.很不幸,当技术表达意味着完全不同的东西的时候,这种非正式的人为表达应该更加恰当.
    或许正是这种技术表达与非正式的人为表达之间的冲突导致了这种混淆.
    在这种非正式的意义上,在基于序列值的字段上索引很容易就会变得”不平衡”,特别是它们被用来表征/处理先进先出(FIFO)队列机制的时候.然而,即使它们(在非正式语境中)是不平衡的,它们(在技术上)仍然是平衡的B-tree.
    (推动使用类似于”扭曲”或者”分布不均匀”作为术语来描述这种类型的索引,或许会是个好主意.)
    有时,仅仅几篇草率地使用术语的文章或报告就可能构建一个神话,在这个例子中,就是一个导致众多DBA浪费无数小时的时间去做不必要的索引重建的神话.
    记住,你下一次认为Oracle表现愚蠢或者低效的时候,很可能问题是出在一个古老的误解上面,而不是Oracle软件本身的问题.

    告警提醒

    如果你还想进一步地研究,treedump选项还有一系列的问题需要注意.对于大部分的Oracle版本,它看似对索引段中的每个块都生成一行输出,这样可能会非常昂贵并且速度缓慢,因为它需要按顺序访问索引中的每一个数据块.然而,在Oracle 9.0中,跟踪文件看似会对每一个块做一个整块转储,这样会使得转储文件非常巨大,转储速度也会非常缓慢.
    第二个问题是所有版本都一致的.如果这个索引是在定义主键约束或者唯一键约束时生成的,Oracle就会设置ind$表的flags字段的第13位,而这将导致treedump程序崩溃并报出错误”invalid value.”分区索引的分区段不会产生这个问题,但是对于所有其他类型的主键索引与唯一索引(包含非分区索引组织表,IOT),这都很恼人.先创建索引,再基于这个索引创建约束通常是个好主意, 这样处理可以避免除索引组织表外的其他所有条件下的问题. 在紧急情况下,你可以修改ind$表来清除这一位,但是很明显,需要先取得Oracle支持的认可.

    结论

    当谈到平衡B-tree索引的时候,术语”平衡”指的是从顶部到底部,而不是从左到右.
    Oracle确实实现了一个版本的”平衡B-tree索引”,因此在任何时候,索引中的所有叶子块到根块的距离都是完全相同的,如果最近对这个索引作过分析的话,可以从视图user_indexes的字段blevel找到它,如果刚刚对这个索引执行过validate index的话,可以从视图index_stats的height字段(等价于blevel+1)得到.
    当听到你应该经常重建索引,因为”这些索引已经变得不平衡”时,要抵制这种理由.因为它不是一个靠得住的理由.

    Some use cases of Cassadra

    2010 February 28
    by jametong

    source : http://n2.nabble.com/Cassandra-users-survey-td4040068.html
    survey By Jonathan Ellis of Rackspace


    user site application others evaluated
    Jonathan
    Ellis-3
    Rackspace  stats collection (testing,
    almost production),Mail & Apps
    division (early testing)
    HBase, Hypertable, dynomite,and
    Voldemort
    Ryan King Twitter storage for all tweets a custom mysql impl, voldemort,
    hbase,mongodb, memcachdb, hypertable, and others
    Edmond Lau Ooyala store and serve our near
    real-time video analytics data
    HBase, Cassandra, Voldemort, and
    some others
    Joe Stump SimpleGeo  real-time location style='mso-spacerun:yes'> 
    infrastructure
    scott w Onespot  a subset of our data store Tokyo, Voldemort and Riak and
    Cassandra
    Vitaly Kushner Astrails  a project for one of our
    clients(early development stage)
    Dan Di
    Spaltro
    Cloudkick  store monitoring statistics
    and
    running analytics over the data
    Eric Lubow ShermansTravel mailing system,social network
    usage
    Cassandra and Tokyo
    Cabinet/Tokyo Tyrant
    Richard
    grossman
    bee.tv a large application related to
    smart TV and movies recommendations,index each day all the TV shows in every
    states + all the new VOD sources like hulu, itunes amazon etc
    matthew
    hawthorne
    Comcast tons of data that we are
    migrating
    into non-relational storage
    cassandra, riak, voldemort, and
    hdfs
    Santal Li Cisco Webex store User Feed & User Activity data Voldemort, MemcacheDB, Dynomite

    为什么Oracle不使用我的索引?!

    2010 February 26

    本文翻译自Jonathan Lewis发表在DBAZine上的文章:Why Isn’t Oracle Using My Index?!,可以从此处下载本文的Word版本.

    为什么Oracle不使用我的索引?!
    by Jonathan Lewis
    标题的这个问题可能是在Metalink论坛与Usenet新闻组出现的最频繁的问题了.这篇文章使用一个测试用例(可以在你自己的系统来重现的)来演示基于成本的优化器的基本工作原理.在看完这篇文章之后,当再次遇到这个令人讨厌的问题时,你应该就可以自信的解答了.
    由于在安装Oracle的时候存在大量的选项,因此当某人执行一条你口授的脚本时,通常很难精确的预测即将出现什么结果. 当时我想要尝试一下,希望你的数据库选择了一个相对普通的安装选项,并且最常用的关键的参数是取得默认值. 这个例子是在Oracle 8.1.7下创建并测试的,参数db_block_size被设置成最常用的值(8k),参数db_file_multiblock_read_count也设置了一个很常用的值(8).在Oracle 9.2下跑图-1中的这个脚本(创建了一组表,在表上添加索引并分些表与索引),结果可能出现部分差异.

    create table t1 as
    select 	trunc((rownum-1)/15)	n1,  trunc((rownum-1)/15)	n2, rpad('x', 215)		v1
    from all_objects
    where rownum <= 3000;
    create table t2 as
    select
        mod(rownum,200) n1,
        mod(rownum,200) n2,
        rpad('x',215) v1
        from all_objects
        where rownum <= 3000;
    create index t1_i1 on t1(N1);
    create index t2_i1 on t2(n1);
    analyze table t1 compute
    statistics;
    analyze table t2 compute statistics;
    

    图 1: 测试的数据集
    在你准备好数据之后,你可能认为这两组数据是一样的,尤其是,在两个数据集中字段N1有的值范围相同(从0-199),并且每个值都出现了15次.你可能会这样检查数据:

    select n1, count(*)
    from t1
    group by n1;
    

    查询表T2也会证实你的上述观点.
    如果你接着执行下面的查询语句:

    select * from t1 where n1 = 45;
    select * from t2 where n1 = 45;
    

    你将发现每个查询语句都返回了15条记录.然而如果你执行

    set autotrace traceonly explain
    

    语句,你将发现这两个查询语句拥有两种不同的执行路径.针对表T1的查询使用了索引,而针对表T2的查询则做了一个全表扫描.
    因此,在拥有完全相同的数据的情况下,同一个查询语句戏剧性地出现了两个不同的执行路径.

    索引到底怎么了?

    注意: 如果你曾经听到如下的关于使用索引的"魔法"准则,例如,"在数据少于23%/10%/2%(随机取一个数字)的时候,Oracle将使用索引,"那么,此时你将怀疑他们的准确性.例如,在这个例子中,Oracle对于一个在3000记录中取15条的查询使用了全表扫描,仅仅0.5%的数据.
    要调查诸如此例的问题,只有一个手段(我经常首先尝试使用的):添加一些索引以使得Oracle选择我们认为它应该选择的执行路径,并检查这能否给我们部分提示.
    在这个例子中,添加一个简单的提示:
    /*+ index(t2, t2_i1) */
    就足够让Oracle从选择全表扫描切换到选择使用索引访问.图-2展示了这三种路径的成本(简化为C=nnn).

    select * from t1 where n1 = 45;
    EXECUTION PLAN
    --------------
    TABLE ACCESS BY INDEX ROWID OF T1 (C=2)
      INDEX(RANGE SCAN) OF T1_I1 (C=1)
    
    select * from t2 where n1 = 45;
    EXECUTION PLAN
    --------------
    TABLE ACCESS FULL OF T2 (C=15)
    
    select /*+ index(t2 t2_i1) */
        *
    from t1
    where n1 = 45;
    EXECUTION PLAN
    --------------
    TABLE ACCESS BY INDEX ROWID OF T2 (C=16)
      INDEX(RANGE SCAN) OF T2_I1 (C=1)
    

    图 2: 不同的查询以及它们的成本
    因此,为什么在T2相关的查询中Oracle没有选择索引作为默认的执行路径?很简单,正如执行计划所展示,选择执行全表扫描的成本要低于使用索引的成本.

    为什么使用全表扫描成本更低?

    当然,这仅仅是在重复问题.为什么全表扫描的成本会低于使用索引的成本呢?
    通过深入调查这个问题,你将揭开了基于成本的优化器的关键机制(也是致命的错误假设).
    让我们通过运行下面这个查询来开始我们的考查:

    select
         table_name,
         blevel,
    	   avg_data_blocks_per_key,
    	   avg_leaf_blocks_per_key,
    	   clustering_factor
    from	user_indexes;
    

    下表是对应的输出结果:

      T1 T2
    Blevel 1 1
    Data block / key 1 1
    Leaf block / key 1 15
    Clustering factor 96 3000

    请特别关注"data block per key"的值. 如果你执行一个完全基于这个索引的键值的等值校验的查询语句,这个值就是Oracle认为必须访问的不同数据块的数量.
    因此,我们的查询语句的成本是从哪儿来得呢?就Oracle而言,如果输入的键值为45,当从表T1获取数据时,我们就可以访问一个索引叶子块以及一个表块(两个块),因此成本为2.
    如果对表T2执行同样的查询,我们就必须访问一个索引叶子块以及15个表块(总共16个块),因此成本为16.
    很明显,根据这种观点,表T1的索引比表T2的索引更加理想.不过,这就带来了两个未解决的问题:
    全表扫描的成本来自何处,为什么两张表之间的avg_data_blocks_per_key的数值差别如此大?
    第二个问题的答案很简单.回头再看看表T1的定义,它使用trunc()函数来生成N1的值,使用"rownum -1"除以15并截成整数.

         Trunc(675/15) = 45
         Trunc(676/15) = 45
               …
         Trunc(689/15) = 45
    

    所有值为45的记录实际上都是一条接着一条连续(可能所有的都能够保存到一个数据块中)的出现的.
    表T2使用mod()函数来生成N1的值,使用对rownum取200的模的方式.

          mod(45,200) = 45
          mod(245,200) = 45
                …
          mod(2845,200) = 45
    

    值为45的记录要隔200才会在表中出现一次(可能导致每一个相关数据块中都不会超过1条记录).
    通过对表的分析,Oracle可以得到我们表中的数据分布的完美的描述.从而优化器才确切的明白,对于我们的查询,Oracle将需要访问多少个数据块,在这个简单的例子中,这个查询的成本就是需要访问的数据块的数量.

    但是为什么是全表扫描呢?

    我们看到,对于同样的执行路径,对表T2进行索引访问的成本要远远高于对表T1的索引访问成本,但是为什么会选择使用全表扫描呢?
    这个问题将让我们发现Oracle做的两个过于简单甚至不恰当的假设.
    第一个假设是,每个块访问都视为一次物理磁盘读,第二个假设是,多块读的速度与单块读一样.
    因此,这些假设将对我们的这个实验产生什么影响?
    如果使用下面的查询语句查询user_tables表:

    select
          table_name,
          blocks
    from user_tables;
    

    你将发现这两张表每个都是占用96个数据块.
    在文章开头,我说过这个测试用例运行在一个db_file_multiblock_read_count的值为8的版本为8的Oracle系统系统上.
    粗略地讲,Oracle认为它可以通过12(96/8)次磁盘读请求来读出所有的96个数据块.
    由于通过索引访问表需要16个块(等于物理读)请求, 从Oracle的可悲的受骗的视角看的话,选择全表扫描显得更清晰也更快捷.
    瞧!如果你要访问的数据适当地散布在表上,即使只有很小比例的数据也会选择使用全表扫描,在数据块非常多(也就是表很大)而返回的记录数很少的时候,这个问题还会被放大.

    校正

    实际上,可能你已经发现,我计算出来的扫描读次数为12,而执行计划中报告的成本是15.一种轻微的简化版本认为表扫描(或者索引快速全扫描)的成本为
    'number of blocks' /
    db_file_multiblock_read_count.

    Oracle使用一种"调整后"的多块读的值来做这种计算(然而,在扫描开始之后,它仍然尽力使用这个真实的请求值来扫描).
    为了方面查询,下表对比了几个真实值与调整后的值:

    Actual Adjusted
    4 3.175
    8 6.589
    16 10.398
    32 16.409
    64 25.895
    128 40.865

    你还将发现,当你为这个参数提供一个不切实际的很大的值后,Oracle可以为你提供保护来避免发生错误.
    顺便提一下,Oracle 9中有一点小小的改变,表扫描的成本还会做进一步的调整(对相除之后的结果加1),这意味着Oracle 9中的表扫描的成本会比Oracle 8中大一点点,从而索引会变得更可能被使用一点点.

    修正

    我们已经看到,优化器有两个内置的假设,而这两个假设又不是很合理.

    • 一次单块读的成本与一次多块读的成本一样-(实际上不大可能,特别是运行在没有顺序的文件系统上的时候)
    • 一次块访问就是一次物理磁盘读-(那么Buffer Cache是干什么吃的?)

    从Oracle 8.1刚发布开始,就有多个参数可供我们以一种相当切合实际的方式来修正这些假设.
    Tim Gorman的文章为这些参数提供了一个切实的描述,下面是简单的描述:

    • Optimizer_index_cost_adj的值范围为1到10000,默认为100.实际上,这个参数描述的是,相对于一次多块读来讲,一次单块读有多么便宜.如果它的值为30,也就是高速Oracle一次单块读的成本是一次多块读的成本的30%. 从而Oracle就会因此这个参数的值很小而更多的倾向于选择使用索引访问.
    • Optimizer_index_caching的值访问为0到100,默认为0.这个参数告诉Oracle,假定索引块将在Buffer Cache中存在百分比.在这个例子中,将这个值设置为接近100的值将助长使用索引而不是表扫描.

    关于这些参数的真正美好的事情是,可以将他们设置成"符合实际"的值.
    将optimizer_index_caching设置成"buffer cache hit ratio"范围内的一个值(你需要自己决定具体是按照default pool,keep pool还是这两个的某种组合来得到这个数值).
    Optimizer_index_cost_adj的值的设置要更加复杂一点.检查v$system_event视图中等待事件"db file scattered read"(多块读取)与"db file sequential read"(单块读取)的有代表性的等待时间.用后者(单块读的等待时间)除以前者(多块读的等待时间)并乘以100.

    改进

    不要忘了,这两个参数可能需要一天(周)的不同时段进行调整以反映终端用户的工作负载.仅仅取得一组数字,就一直使用下去,是不可行的.
    很高兴,在Oracle 9中,情况得到了改善.你可以收集系统统计信息,通常就包含以下四个统计数据:

    • 单块读的平均读取时间
    • 多块读的平均读取时间
    • 实际发生的多块读的平均读取块数
    • CPU的理论可用速度

    要详细介绍这个特性足够配得上一整篇文章,但是这里我特别强调一点,前三个统计值使得Oracle可以明白对多块读的真实成本(相对单块读来讲).实际上,CPU速度使得Oracle可以得出不适宜的访问机制的CPU成本,比如,读取一个数据块中的每条记录以找出特定的数据值,以及与此相似的行为.
    当你将系统升级到Oracle 9时,你首先需要检查的事情就是是否正确使用系统统计信息.单单这个特性就可能大大降低你尝试"优化"的糟糕的SQL的时间.
    顺便提一下,尽管系统统计信息带来了惊人的效果,这两个优化器调整参数仍然有效,虽然使用它们的确切地公式在Oracle 8与Oracle 9之间发生了变化.

    主题的变种

    当然,我选择了一个非常特殊的例子,一个单列非唯一索引上的等值查询,并且表中没有空值,这种情况非常容易处理.(我甚至都没有提及索引的blevel与clustering_factor.)Oracle还有多个不同的方法来处理更加一般的例子.
    考虑如下这些我为了方便而忽视的情况:

    • 多列索引
    • 使用多列索引中的部分列
    • 范围扫描
    • 唯一索引
    • 由非唯一索引代表的唯一约束
    • 索引跳跃扫描
    • 只查询索引的语句
    • 位图索引
    • 空值的影响

    这个列表还可以不停地列下去.并没有一个简单的公式来告诉你Oracle是如何计算它的成本,只存在一个通用准则,通过它你可以了解这个方法的梗概,以及一组可以应用到不同情形下的不同计算公式.
    不管怎样,本文的目的是让你知道有这个通用准则,以及优化器策略中内嵌的两个基本假设.我希望,这篇文章可以帮助你更加深入的理解那些优化器做出来的众所周知的怪事.

    进一步阅读

    • Tim Gorman: www.evdbt.com. "The Search for Intelligent Life in the Cost Based Optimiser."
    • Wolfgang Breitling: www.centrexcc.com. "Looking under the hood of the CBO."

    如何提高SQL 查询技能

    2010 February 24
    tags:
    by jametong

    Robert Vollman 今天发表了一篇由其个人经历总结出来的如何写好Oracle SQL查询的Blog, 本文是对其主要观点的摘录, 这也是Oracle初学者在写SQL是经常遇到的困难.
    Improving your SQL Queries By Robert Vollman

    • 记录详细的需求文档
    • 在写SQL之前必须弄清楚需求, 具体要取什么数据, 有些什么具体的约束条件, 在数据仓库环境中还可以考虑补上这个需求具体对应哪些报表, 对应的基础表如何. 到开发环境的话, 可以考虑加上这条SQL服务于哪些业务(页面),调用频率如何.

    • 不要重新制造轮子
    • 对于一些已经比较成熟的解决方案,有比较现成的例子的SQL,要善于利用已有SQL,已有模板.

    • 降低语句的复杂度
    • 有些同学可能比较喜欢使用比较炫的技术,比较炫的SQL来解决问题. 但是要切记一点, 使用过于复杂过于新的技术, 如果不能在性能(以及其他方面)带来好处的话, 只会使得这条SQL难于维护, 使得其他相关人员难于理解.

    • 小心处理NULL
    • NULL在Oracle数据库中是一个非常特别的值, 它不等于任何值, 所以如果你的SQL返回的值数量偏少,或者根本不对很可能就是使用NULL出现了问题..
      常见的情况是:
      1. 查询的时候直接使用条件 colx = xxx,而这个colx里面是有NULL值的, 这种情况下查询的返回结果是不会包含NULL值对应的记录的, 如果要查询出NULL对应的记录, 需要使用 colx is null (is not null).
      2. var 为null的时候, 在plsql中给var赋值, var := var + xxx;这种情况下var的值会一直是null的, 这一点需要特别注意, 我自己也犯过好几次这个错误.

    • 自己核对数据类型
    • 在where条件里面要仔细地核对数据类型, 由于隐形转换的问题, 在数据类型错误的时候, Oracle无法正确使用索引, 可能会导致SQL运行非常慢.

    • 小心处理重复数据
    • 在需求明确的情况下, 如果你不在乎是否出现重复记录, 或者明确知道不会出现重复数据的情况下, 尽量使用Union All而不是Union进行查询, Union会涉及到昂贵的排序操作.

    • 避免不必要的优化操作
    • SQL的性能调优可能非常有趣非常带劲, 但是很多时候调优可能意义不大, 比如对于只会使用一次的查询, 你可能很少在乎是1秒钟结束还是2秒钟结束..

      不过一些基本的优化规则还是要用的:
      只查询你需要的字段, 而不要所有的查询都是用select *来进行.
      在通过索引来查询更合适的时候, 尽量在查询条件中指定有索引的字段来查询. (在返回的记录条数很少的时候, 使用索引一般都能更加快速的得到查询结果.
      不要可以避免使用表连接. 关系数据库就是为了表连接而设计的.

    • 尽可能使用绑定变量
    • 在开发环境使用的SQL语句尽量使用绑定变量, 这样可以大大缓解Oracle数据库解析SQL的消耗, 也可以大大提高数据库的可扩展性.

    • 使用源码控制工具
    • 最好使用CVS/SVN一类的源码控制工具来管理你的SQL/PLSQL代码, 这对于后期的维护有非常大的帮助, 也有助于其他人更好的理解你最初写这条SQL的意图.

    • 测试,测试,测试.
    • 在SQL写好之后, 要深入的进行测试, 以确保其正常运行.

    闪存表空间 VS 数据库Flash Cache

    2010 February 22

    本文翻译自Guy Harrison的blog: Flash tablespace vs. DB Flash Cache, 这是他写的关于Flash Cache系列文章的最后一篇,另外还会翻译两篇Kevin Closson写的关于Flash Cache的相关文章.
    之前两篇关于Flash Cache的文章如下:

    闪存表空间 VS 数据库Flash Cache

    在这篇文章中,我将根据我最近针对使用SSD作为数据文件的存储以及使用Oracle 11GR2数据库Flash Cache所做的测试,给出一份两者的性能对比.
    有时,我的整个职业生涯看上去都是在等待旋转磁盘的终结.这项技术是如此古老,能力限制如此明确,如此机械.因此,SSD作为一种数据库存储介质越来越可行(Oracle 11GR2已经直接支持这一点),这个事实令人振奋.
    使用SSD作为数据库存储的一部分确实会产生很大的问题,但是,理解闪存SSD的性能特征却是非常重要的,它可以帮助我们确保不会不当地使用它.
    SSD有以下两个特征:

    • 基于闪存的SSD使用与常见的USB盘相似的闪存技术,这种USB盘已经在小容量移动数据存储领域替代软盘.闪存RAM比较便宜,提供不需要电池备份的持久存储,因此其耗电量也很低.
    • 基于DDR RAM的SSD使用本质上与服务器核心内存差别不大的内存模块.这种RAM需要有持久存储(如磁盘或闪存RAM)和内部电池来支撑.在发生电力故障的时候,电池可以提供足够的电力来保证可以将RAM内存中的内容写到持久存储.

    DDR SSD非常昂贵(以及$$/GB这个级别),以致于目前无法作为专业的数据库设备使用.但是,基于闪存的SSD磁盘越来越称为机械磁盘的一个可行的替代选项.

    读,写以及擦写操作

    闪存盘存储是按照页(一般为4K)以及块(一般为256K)来组织的.对于读操作来讲,闪存盘可以从单个页(page)快速返回结果.往一个页中写数据要慢很多(可能要慢10倍).然而,只有在块中刚好有一个空闲的页的往页写才能达到这个速度.如果我们需要往整个块写数据,必须先清除块内的内容才可以.维基百科关于SSD的条目给出了下面这个关于查找/写以及擦写的时间:

    当一个闪存SSD盘渐渐填满数据时,需要清除操作的块级别的写操作的比例逐渐增加,闪存SSD的写性能也相应下降.

    TRIM API函数

    高端的闪存SSD支持一种叫做TRIM的API,这个功能使得OS可以主动提前清除整个块,从而写操作可以在只有一个页级别的IO内完成.大部分高端的SSD盘还支持一种防磨损算法,这种算法可以在设备上移动热点页以避免块级别出现故障的风险.闪存盘在块变得不可靠之前只支持一定次数的擦写操作,加入磁盘可以自动将热点页在物理存储上移动时,这个缺陷就可以得到缓解.

    MLC vs SLC

    廉价的闪存盘一般都使用MLC(Multi-Level-Cell)技术,它可以实现在一个单元中存储两位的数据,而使用SLC时一个单元中只能保存一位数据.MLC的效果是以牺牲性能的代价来提高数据密度,特别是写性能.从数据丢失的角度讲,MLC也是更加不可靠的.如果你关心写性能,那么或许你应该避免使用基于MLC的SSD.
    通常,如果你想要一个高性能的闪存SSD的话(如果它不是高性能的,干嘛还要它呢?),你就应该选择基于SLC的闪存SSD,并且是支持TRIM API以及有着好的防磨损能力的SSD.在我的测试中,我使用一个Intel X-25 E 32GB的SSD盘.它大概需要600澳元(大概534美元).

    读写速度差异的问题

    假设大部分数据库都是读比写多,我们还需要担心闪存SSD在查找时间与写时间方面的差异吗?毫无疑问答案是YES.对于一个Oracle数据库来讲,当通过Buffer Cache处理事务活动时,一个设备的读能力与往这个设备的写能力之间有很大的不匹配会非常有害.
    这个问题与Buffer Cache中的数据的缓存有关.如果往Buffer Cache中放入数据块比从里面写出简单很多,那么Buffer Cache就很可能会被脏块填满,从而出现free buffer waits等待.下图展示了free buffer waits是如何出现的:

    如果使用的是廉价的闪存盘,那么写速度就会比读速度慢更多,最终free buffer waits等待将成为事务活动高峰时期的限制因素.

    Oracle数据库Flash Cache

    Oracle的数据库Flash Cache提供了另外一种利用闪存SSD的途径. 它不是将整个数据文件放到闪存上,而是将闪存作为二级缓存使用.Flash Cache可以非常大从而加快经常被访问的数据块的读速度.但是,如果闪存盘非常繁忙的话,Oracle就只是尽量少写缓存.这样,我们就可以得到闪存来优化读操作的好处,而不用承担多少写操作带来的损失.
    我在前一篇文章中总结了Flash Cache的处理算法,下面是我在那篇文章中使用的图表,它概括了当数据库使用Flash Cache时一个数据块的生命周期.

    这个架构的关键点是,只有在DBWR没有超负荷时,它才会往Flash Cache中写入数据块.当DBWR逐渐变得繁忙时,往Flash Cache中的写操作将被忽略(这将会降低Flash Cache的效率),它可以防止Buffer Cache被脏块填满,从而导致free buffer waits等待事件的出现.

    闪存盘的读性能

    让我们来看在实际操作中它是如何表现的.下面来看当我们针对如下情况执行500,000次随机索引读取时的性能对比:

    • 1. 一个在机械磁盘上的表,不使用Flash Cache
    • 2. 一个在机械磁盘上的表,使用Flash Cache
    • 3. 一个直接存储在闪存盘上的表

    这个机械磁盘是一块希捷7200.7 80GB Barracuda磁盘,同时这个SSD盘是一块Intel X-25 E 32GB盘(一块非常高端的SLC盘).在这两种情况下,表空间都是创建在裸设备上从而避免文件系统缓存的影响,并且重做日志(Redo Log)以及其他表空间都放置在一个独立的磁盘上.
    下面是相关的读性能:

    如你所料,将表直接放置在闪存上是最快的,因为这里只有读IO,并且每个读IO都是针对这个非常快的闪存SSD.注意,Flash Cache也同样带来了很大的好处,数据库文件的IO数量下降了,当然从Flash Cache读取的速度是非常快的.

    更新性能

    下面,我将测量基于主键的更新操作的性能.这样一个更新操作的性能是由读性能(读取这个数据块到缓存中)与DBWR性能共同决定的.如果DBWR写出脏块的速度不是足够快的话,就会出现free buffer waitswrite complete waits等待事件了.
    我最初预计,对闪存表空间的测试肯定会遇到大量的free buffer waits,因为理论上讲闪存的写操作要比闪存的读操作慢很多很多. 我认为使用Flash Cache将会避免这个问题,并且能够提供更好的性能. 然而,结果非常出人意外.

    我使用不同的负荷重复进行上面的测试,但是每一次结果都非常相似.然而,使用Flash Cache还是要比不使用Flash Cache来得好,将表直接存储到闪存储存上性能就更好了.X-25 E SSD盘支持一个远远超出我预期的写频率(大概2000次写操作/秒).Intel 宣称(现在我相信了)他们拥有精密的算法可以有效避免通常与闪存SSD存储介质有关的写损失.
    注意,实际上大部分的free buffer waits等待事件的产生都是由Flash Cache的配置导致的.Flash Cache使得Oracle的逻辑读的速度更快,但是由于物理写仍然会面对相对更慢的机械旋转磁盘,Buffer Cache常常会被脏块所填满.
    或许随着时间的流逝,当所有的块都被至少写过一次,清除操作变得更加普遍时,X-25的性能会出现下降.然而,如果TRIM API能够正常工作,那么理论上这种性能恶化应该可以避免.注意,并不是所有环境都支持TRIM API,并不是所有的SSD都支持,旧版本的Windows操作可能也不支持.

    Write complete waits: flash cache

    在上一篇文章中,我注意到,当DBWR繁忙的时候会跳过而不写Flash Cache,这一点应该可以避免Flash Cache的活动不会成为导致free buffer waits的直接原因.然而,我观察到,在特定的负荷下会遇到大量的”write complete waits: flash cache“等待事件.例如,下面的输出结果显示大约有75%的实耗时间(elapsed time)消耗在这个等待事件上.

    当一个会话想要修改一个数据,但同时DBWR又正在将这个数据块写到数据文件的时候会出现write complete waits等待事件.Flash Cache相对应的等待事件的出现也是由于类似的原因,但这是不是写数据块到数据文件,而将由DBWR将它写到Flash Cache.当一个特定的数据块被非常频繁的修改的时候,这种现象就会出现;在这种情况下,数据块在被DBWR写完之前被修改的机会相当高.

    结论

    X-25 E闪存SSD盘给我留下了非常深刻的印象.如果它可以长时间的保持优异的写吞吐量的话,相比于传统的机械旋转磁盘以及11GR2的数据库Flash Cache,它提供的吞吐量有惊人的优势.不过,还有几句忠告:

    • 我没有长时间对这个SSD盘做压力测试.有些闪存盘在磨损后显示出更长写延迟.理论上,Intel X-25 E的TRIM功能应该可以避免这一点,但我并没有在这些测试中验证这一点.
    • 在经过长时间的使用后,闪存盘可能会成为一种不可靠的存储介质.当然,任何磁盘都会损坏,但是闪存盘比机械磁盘更加容易损坏(虽然在损坏之前它可能可以做更多的工作).
    • 在我的测试中,我的表很小,足以完全存放在闪存存储上.如果做不到这一点,使用数据库Flash Cache让我们在无法将整个数据库都放到SSD上的时候,也可以利用闪存盘.
    • 现在,我更加热衷于使用闪存盘了.只要你购买一个高性能的SSD盘(考虑SLC & TRIM),你就可以考虑将整个表空间或者数据库都存放在这些盘上.如果担负不起所有的数据都存放在高端的闪存盘上,还可以考虑使用11GR2的数据库Flash Cache来获得显著的性能提升.

    推荐几篇不错的解释Oracle基础概念的文章

    2010 February 21
    by jametong

    一组关于lgwr处理过程相关的文章, 主要是强调log file sync等待的主要原因不是lgwr io而是lgwr processing.

    Tuning ‘log file sync’ wait events By Riyaj Shamsudeen

    LGWR I/O is Simple, But Not LGWR Processing By Kevin Closson

    Don’t Forget ARCH When Pondering Redo By Kevin Closson

    The “log file sync” wait event is not always spent waiting for an I/O By Christian Bilien

    一篇Jonathan Lewis写的关于Undo设计的文章.
    Why undo By Jonathan Lewis