Recent Posts

RSS Feeds

DBA日记第三部 像Oracle一样思考 4月2日 索引危机(2)索引范围扫描的优化方法

请到下面网页阅读:

http://www.oraclefans.cn/forum/showtopic.jsp?rootid=19756

 

在这里老白做个广告:

DBA日记第二部实体书《ORACLE RAC日记》已经由人民邮电出版社出版,请到下列地址订购:

  http://www.china-pub.com/50901

http://item.taobao.com/auction/item_detail.htm?item_num_id=5059262372

DBA日记第一部实体书《Oracle优化日记》6月13日正式出版,敬请关注

 

Permalink     No Comments

DBA日记第三部 像Oracle一样思考 4月1日 索引危机(1)不能修改应用的项目

请到oraclefans网阅读:

http://www.oraclefans.cn/forum/showtopic.jsp?rootid=19716&CPages=1

 

Permalink     No Comments

DBA日记第三部 像Oracle一样思考 3月29日 理解索引(3)索引访问的方式

330日 理解索引(3)索引访问的方式

设计索引是专业性很强的工作,一般来说索引设计最好有专业DBA参与,不过现实中,绝大多数系统的索引设计都是由开发人员完成的。由于开发人员对索引的原理不甚了解,因此在大多数软件系统中索引设计方面都存在很大的问题。Oracle公司有一门培训课程叫做《开发DBA》,这门课程的主要目的是让开发人员理解一些Oracle的基本原理与概念,从而在设计应用系统的时候可以依据这些原理,避免出现一些严重的问题。在开发人员中进行类似《开发DBA》这样课程的培训,是数据库优化工作中十分重要的环节,而这个环节往往被我们所忽视。实际上,在研发团队进行适当的培训,可以大量节省的成本。

今天我们要讨论的话题是如何设计索引,这是很多DBA都很想了解的。实际上昨天我们也讨论到了设计索引的一些基本的方法。索引的设计要遵循一些最基本的原则,首先,索引的设计必须是有针对性的,索引的目的是快速定位数据,因此每个索引都必须符合快速定位数据的要求。比如说我们想让下面的一条语句执行的更快一些,消耗更小的资源:

select emp_name,emp_id,sal from emp where emp_id=11023

首先我们需要分析一下emp这张表,emp表是企业的职员表,有数万条记录,而每个职员都有唯一的EMP_ID对应,那么如果我们在EMP_ID上创建了索引,那么这个SQL就可以通过索引快速定位到EMP_ID,然后通过索引中的ROWID直接找到EMP表中的相关记录,并取出所需要的数据。这种情况下,在EMP_ID上创建一个索引就是十分必要的。我们再来看下一个语句:

select emp_id,sal from emp where emp_id=11023 and emp_name like 'John%'

在这个语句中,WHERE条件有两个,一个是emp_id=11023,另外一个是emp_name likje 'John%'。这个时候oracle该如何来选择合适的执行计划呢?我们讨论的前提是使用CBO优化器(RBO优化器在索引选择的时候智能程度很低,出现执行计划错误的机会要比CBO大得多,因此建议在有可能的情况下,尽可能使用CBO),在使用CBO的情况下,CBO会根据索引的统计数据计算每个索引访问路径的成本,从而选择一种开销较小的执行计划。在这个案例中,我们可以简单的来分析一下。如果通过EMP_ID来查找相关的数据,EMP_ID是主键,根据EMP_ID=11023可以唯一定位到一条记录,然后我们可以取出这条记录的SALEMP_NAMEEMP_ID这三个字段的值,将EMP_NAME字段的值和WHERE 条件中的emp_name like 'John%'进行比较,如果这条记录符合这个条件,就返回这一条记录,如果这条记录不符合,那么就没有记录返回了。

下面我们来分析一下另外一条访问路径,如果我们使用EMP_NAME上索引,首先找到所有的emp_name like 'John%'的记录,也许我们能找到10条记录,通过索引中提供的10条记录的ROWID,再去访问这张表,把emp_id,sal这两个字段的值取出来,然后我们再通过另外一个过滤条件emp_id=11023进行筛选,把符合条件的记录找出来。很明显,我们可以发现这条访问路径的开销要比第一条略高,因为我们先查出了10条记录,然后再通过过滤器过滤掉了其中的9条,而第一种执行计划只检索了一条记录就获得了结果。

第三种访问路径就是我们所说的全表扫描,如果在EMP_NAMEEMP_ID上都没有创建索引,那么我们必须对这张有几万条记录的表从头到尾扫描一遍,找到所有符合上面两个过滤条件的记录,并把对应的EMP_IDSAL值检索出来。

这三条访问路径到底哪个开销更小呢?实际上ORACLECBO优化器会自动计算访问路径的成本,并且选择最佳的路径来执行这个SQL。实际上我们的例子里包含Oracle访问数据的三种常见的方法,第一种是索引唯一性检索,通过唯一性索引定位到某条记录,读出该记录后通过emp_name这个过滤条件进行筛选,最后获得符合条件的所有记录。第二条路径是通过emp_name上的索引找出所有符合条件的记录,这种查找方式是找到符合条件的第一条记录,然后顺着叶节点链按照升序或者降序的方式扫描出所有符合条件的记录的ROWID,然后依次将表中的相关数据块读出来,根据其他的过滤条件进行筛选,找到符合条件的记录,这种方式也就是我们常说的索引范围扫描。第三种方法是直接读取表的数据,将数据读出后,根据emp_idemp_name两个条件进行过滤,获得所需要的数据。

另外还有一种数据访问路径,在这个案例中看不到,这种方法是Oracle 9i开始支持的。比如我们有一个复合索引IDX_ID_NAME,这个索引包含两个字段EMP_IDEMP_NAME,而有这样一个SQL:

select emp_id,sal from emp where  emp_name= 'John'

 

这种情况下,我们可以通IDX_ID_NAME来查找所需要的数据。由于这个索引中,EMP_NAME不是这个索引的第一个字段,因此在扫描索引的时候无法像普通的UNIQUE 或者RANGE SCAN一样,通过一次定位,然后顺着叶节点链进行扫描。这种扫描必须是跳跃式的,因为这个索引的前导字段是EMP_ID,因此扫描的时候,对于每个EMP_ID,都需要做一次定位,然后通过叶节点链查找到所有符合条件的记录。Oracle也给这种扫描方式起了一个很形象的名字:INDEX SKIP SCAN,中文翻译一般使用“索引跳跃式扫描”。INDEX SKIP SCAN是一种跳跃式的扫描方式,因此分段的数量对于扫描的成本影响很大,本案例中EMP_ID是主键,在这种情况下,使用INDEX SKIP SCAN的成本实际上是比较高的,其成本开销甚至会高于索引全扫描。

刚才我们又引入了另外一种索引访问的方式,索引全扫描。索引全扫描有两种不同的方式。一种被称为索引全扫描(INDEX FULL SCAN),另外一种被称为索引快速全扫描(INDEX FAST FULL SCAN)。为什么要使用索引全扫描呢?我们先看一个SQL

select emp_name from emp where emp_name is not null

emp_name上有一个索引,我们知道索引中包含了所有emp_name不为空的值,因此这种情况下,只要对整个索引进行一次扫描就可以完成这个SQL了,而不需要对表做全表扫描。一般来说,对于字段较多的表,索引的大小会远小于表的大小,因此全索引扫描的成本会远小于全表扫描的大小。上述这个例子中,可以使用索引快速扫描,对索引的扫描可以根据该索引的extent来进行,采用多块读的方式进行。因此在这类操作中,我们可以看到会话会大量的出现db file scattered read等待。我们经常看到db file scattered read等待就说系统中出现了全表扫描,其实这种说法并不准确,因为索引快速全扫描也会使用多块读的方式来扫描。

如果我们对SQL进行一些小的修改:

select emp_name from emp where emp_name is not null order by emp_name

那么SQL的执行路径可能会有所改变,不使用索引快速全扫描。索引快速全扫描是根据EXTENTS的顺序的,不是按照EMP_NAME的值的大小进行的,因此这样扫描出来的emp_name数据不是按照emp_name排序的,如果要排序,必须对索引的扫描完成后再进行一次排序。这样的执行计划可能不如另外一种索引扫描的方式-索引全扫描,索引全扫描和索引快速全扫描不同的地方是,索引全扫描是根据叶节点链来进行的。进行索引全扫描首先要从根开始,找到叶节点链上的第一个数据块,然后沿着叶节点链进行扫描,由于叶节点链是根据索引键值排序的,因此这样扫描出来的数据本身就是排序的,数据读出后不需要再次排序。这种扫描方式和索引快速全扫描相比,首先要找到索引的根,然后通过枝节点找到第一个叶节点,然后再顺着叶节点链扫描整个索引。索引全扫描的IO成本比索引快速全扫描要大很多,读取根节点和叶节点的成本相对不大,不过由于顺着叶节点链扫描整个索引的时候无法使用多块读,而只能使用单块读,因此这种扫描方式的IO开销要远大于索引快速全扫描。这种索引扫描,我们如果对会话进行跟踪,会发现大量的db file sequential read等待。

在这里,大家可能会有一个疑问,带ORDER BYSQL语句是否一定使用索引全扫描成本小于索引快速全扫描呢?实际上这也不能一概而论,关键要看排序操作的成本是否大于索引单块扫描比索引多块扫描两种扫描方式之间的IO成本差。

谈到这里,我们已经基本了解了索引访问的主要的方式,实际上还有一种索引的访问操作我们还没有讨论,就是位图索引的访问。由于位图索引的结构十分特殊,位图索引没有枝节点,是一种平面结构,因此如果我们想要通过位图索引进行扫描,只有一种扫描方式,这种访问方式类似于普通索引的索引快速全扫描,就是将索引按照EXTENTS,通过多块读进行扫描,这是因为位图索引的每个数据块中都可能包含我们所需要的键值。

 

Permalink     No Comments

这几天挺烦,DBA日记好久没更新了,大家原谅

这几天烦人的事情比较多,又经常半夜被电话吵醒。所以很少在网上发言,DBA日记也好久没更新了。可能是40多岁了,体力确实不如前些年,碰到一些比较复杂的问题,也不如前些年那样感到兴奋了。国内做DBA的环境和国外相差甚大,在国外,DBA做到40多岁,应该是最成熟的时期,事业有成,不必要为生活而忧虑,可以一心做些自己想做的事情了。在国内这方面就差了很多,我虽然现在也算衣食无忧,不过也不敢得罪任何一个衣食父母,因此还无法像庄子一样随心所欲。

这几天事情很多,感觉很疲乏,所以上网也希望聊些不需要动脑子的,不想讨论太深入的问题,可能怠慢了一些网友,在这里老白给大家道个歉了。前天晚上回到家里,一进家门,老婆就问我一个比较头疼的问题。我马上说,今天晚上谁都别和我说话。因为我知道,这种精神状态下,一旦几句话说的不和我的胃口,我就可能会发火。

 

实际上,这也是DBA生活的一部分,不仅仅像DBA日记里老白所说的那么阳光灿烂,等有时间,我也许也应该把这部分情感写出来和大家分享。

Permalink     2 Comments

DBA日记第三部 像Oracle一样思考 3月29日 理解索引(2)反转键索引的误区

请到下列网址查阅:

http://www.oraclefans.cn/forum/showtopic.jsp?rootid=18643

 

由于这个BLOG系统对于较长的文档排版存在问题,因此暂时只发链接。不便之处请原谅

Permalink     1 Comment

DBA日记第三部 像Oracle一样思考 3月28日 理解索引(1)

请到ORACLE粉丝网上阅读

http://www.oraclefans.cn/forum/showtopic.jsp?rootid=18555

Permalink     No Comments

DBA日记第三部 像Oracle一样思考 3月27日 简单任务 (4)更大的意外

想了解这个项目最终的实施方案和效果吗,请到粉丝网上阅读:

http://www.oraclefans.cn/forum/showtopic.jsp?rootid=18456&CPages=1

Permalink     No Comments

DBA日记 第三部 像Oracle一样思考 3月26日 简单任务 (3)令人惊讶的结果

可以到Oracle粉丝网阅读本节:http://www.oraclefans.cn/forum/showtopic.jsp?rootid=18190

    最近系统的业务量不大,所以客户也同意我们白天就做测试。为了防止误操作,客户把SCOTT账号提供给我做测试。今天我准备做几个测试:

    • l         SEQUENCE缓冲区测试
    • l         表的FREELISTSINITRANS参数调整的测试
    • l         HASH分区测试
    • l         提交批量测试:测试批量大小对插入性能的影响,分别测试批量为800150030005000条记录的响应时间
    • l         BULK INSERT操作测试

做这种单条SQL执行时间很短的测试,最好的办法是使用profiler工具,将要测试的内容写在一个存储过程里,通过profiler工具来计算平均执行一次所消耗的时间。我首先为每个测试项目都谢了一个小的PL/SQL过程,然后开了7个终端,运行这个存储过程,在第八个终端上的测试过程与其他不同,增加了PROFILER的脚本,这一就能够很方便的采集到每条SQL的执行情况了。

首先测试SEQUENCE,我分别对各种CACHE值进行了测试。首先编写了一个测试用的存储过程:

create or replace procedure testSeq(N integer)

is

 i integer;

 b integer;

 v varchar2(20);

begin

  i:=0;

  v:=to_char(sysdate,'yyyy-mm-dd:hh24:mi:ss');

  dbms_output.put_line(v);

  loop

    exit when i>N;

    i:=i+1;

    select sm_idseq.nextval into b from dual;

  end loop;

  v:=to_char(sysdate,'yyyy-mm-dd:hh24:mi:ss');

  dbms_output.put_line(v);

end;

/

 

在第八个终端上,执行下面的脚本:

declare

    err number;

begin

   err:=DBMS_PROFILER.START_PROFILER (‘test seq 1000’);

   testseq(200000);

   err:=DBMS_PROFILER.STOP_PROFILER ;

end;

/

脚本执行结束后,可以通过下列脚本查看存储过程中每一行执行的情况:

 

column RUN_COMMENT format a40 truncate;

select runid, run_date, RUN_COMMENT from plsql_profiler_runs order by runid;

column unit_name format a15 truncate;

column occured format 999999 ;

column line# format 99999 ;

column tot_time format 999999.999999 ;

 

select p.unit_name, p.occured, p.tot_time, p.line# line, 

       substr(s.text, 1,75) text

  from 

       (select u.unit_name, d.TOTAL_OCCUR occured, 

               (d.TOTAL_TIME/1000000000) tot_time, d.line#

          from plsql_profiler_units u, plsql_profiler_data d

         where d.RUNID=u.runid and d.UNIT_NUMBER = u.unit_number

           and d.TOTAL_OCCUR >0

           and  u.runid= &RUN_ID) p,  

       user_source s

 where p.unit_name = s.name(+) and  p.line# = s.line (+) 

 order by p.unit_name, p.line#;

 

其中的参数,run_id来自于plsql_profiler_units,可以通过我们执行PROFILER的时候使用的名称来查找刚才的测试对应的run_id,一般来说还有个更简单的查找方法,就是找最后一个run_id,因为run_id是通过sequence产生的,我们刚刚做过的测试肯定是最后一个。

上面的查询的结果如下:

UNIT_NAME       OCCURED       TOT_TIME       LINE TEXT                                                                                                                                                  

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

<anonymous>           1        .000781          4                                                                                                                                                       

<anonymous>           1        .009996          5                                                                                                                                                       

<anonymous>           1        .002121          6                                                                                                                                                       

TESTLOG               1        .001145          6   i:=0;                                                                                                                                              

TESTLOG           50001      40.602085          8     exit when i>=N;                                                                                                                                  

TESTLOG           50000      59.097742          9     i:=i+1;                                                                                                                                           

TESTLOG           50000    2948.819922         10     select sm_idseq.nextval into v from dual;                                                                                                         

 

PROFILER可以计算出每一行执行的次数,以及总共消耗的时间。从而可以为我们提供准确的测试数据。SEQUENCE的测试结果如下:

CACHE

并发数量

执行次数

测试时间(毫秒)

平均每次执行时间

2

8

200001

942122

4.7

100

8

200001

73282

0.366

1000

8

200001

48250

0.241

5000

8

200001

47129

0.236

20000

8

200001

43299

0.216

从测试结果来看,sequence缓冲区的增加可以提高sequence的访问性能,不过超过1000后,加大cache参数,性能提升幅度不大。目前sequencecache已经达到了1000,再加大cache,对性能影响不大。看样子sequencecache是不需要再加大了。

第一项测试虽然也在预料之中,不过测试结果还是让我感到有点失望。哪怕能提升23%也是好的啊,看样子只能寄希望于后面的测试项目了。第二项测试的是sm_histable表的核心参数,测试比对的是完全按照目前的参数创建的一张测试表,和修改了freelists,initrans,initial,next这几个参数的测试表。表的分区方式,以及存储的表空间等属性都没有修改,索引也完全按照生产环境创建。这次测试的结果让人感到十分惊诧:

项目

相关业务

调整前(秒)

调整后(秒)

对比说明

整体时间

短信历史记录应用:

DB_DaeMon程序

128.67

112.35

速度提升:14.53%

平均每条记录的插入时间

 

0.002573

0.002247

速度提升:14.53%

调整这几个参数后,并发插入的性能居然提升了14.5%,这有点出乎我的预料。基本上达到了我预期的最高值。兴奋之余,我马上进行了HASH分区的测试,我修改了这张表的定义,将表分区从10个修改为8个,分别存储在4个表空间上,这4个表空间分别属于不同的RAID组,我并没有将5RAID组全部使用,因为另外一个RAID组上,存放了REDO LOG文件,这么设计是为了达到REDO LOG和数据文件互相不干扰的目的。

PARTITION BY HASH (ID_HINT)

 PARTITIONS 8

 STORE IN (CQYDSMSC_CENTER1,CQYDSMSC_CENTER2,CQYDSMSC_CENTER3,CQYDSMSC_CENTER4)

测试的结果如下:

项目

相关业务

调整前(秒)

调整后(秒)

对比说明

整体时间

短信历史记录应用:

DB_DaeMon程序

90.397

78.793

在存储参数提升14.53%的基础上,再提升14.73%

Permalink     No Comments

DBA日记 第三部 像Oracle一样思考 3月25日 简单任务 (2)解决之道

大家也可以到 http://www.oraclefans.cn/forum/showtopic.jsp?rootid=18186 去阅读

3月25日 简单任务 2)解决之道

今天一早老方就回北京了,昨天晚上我和老方一直在讨论这个项目,从采集到的信息来看,只能通过调整操作系统、数据库的参数,以及调整表的一些参数来达到提升系统总体性能的目标。而对于系统性能来说,最为关键的也就是大量的话单插入操作。目前话单插入是由8个并发进程完成的,每次的插入批量试800条记录。

既然实际情况和预先估计的有出入,那也只能硬着头皮上了。今天我主要要分析一下在哪些环节可以进行优化,以及每个环节可能的性能提升比例,从而为优化计划的制定提供一个基础。另外采集性能基线的工作也同时在进行,由于这个项目的特殊性,我们无法采集去年春节期间的数据了。这种系统,非业务高峰期的性能数据比较的意义不是很大。通过昨天的分析,在平时,每天的话单量在2000万条左右,而在中秋节和除夕,话单量可能达到1亿8千万到2亿。按照忙时集中系数为0.1计算,在平时,每天最忙的1个小时内,产生的话单量为200万条,折算到每秒钟产生的话单量为555条,而业务高峰期的量可能达到5550条。平均分配到两个系统,每个系统每秒要处理近3000条话单,才能确保系统不出问题。由于华为的系统中有内存缓冲区,可以进行一定程度的平滑高峰处理,因此华为方面提出能够达到每秒处理2000条话单以上的处理能力,就可以保证不丢话单,而目前华为工程师的测试结果为系统处理能力仅能达到1500条话单,这个优化项目,能够做到性能提升50%,就可以确保万无一失,提升25%以上,可以基本达到华为的最低要求。仅仅想通过调整系统参数和表的存储参数来实现这个目标,确实是具有一定的挑战性。

上午我分析了系统的主要等待事件,以及各个缓冲区的情况:

Instance Efficiency Percentages (Target 100%)

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

            Buffer Nowait %:   99.98       Redo NoWait %:  100.00

            Buffer  Hit   %:   99.69 In-memory Sort %:   99.97

            Library Hit   %:   99.79        Soft Parse %:   88.82

         Execute to Parse %:   98.61         Latch Hit %:   99.98

Parse CPU to Parse Elapsd %:   93.32     % Non-Parse CPU:   98.51

 

 Shared Pool Statistics        Begin   End

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

             Memory Usage %:   93.70   95.50

    % SQL with executions>1:   35.74   34.34

  % Memory for SQL w/exec>1:   29.93   21.30

 

Top 5 Wait Events

~~~~~~~~~~~~~~~~~                                             Wait     % Total

Event                                               Waits  Time (cs)   Wt Time

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

db file parallel write                              2,922       36,456   47.99

db file sequential read                            16,401       15,874   20.90

log file parallel write                             8,824       10,812   14.23

log file sync                                       2,907        5,169    6.80

direct path read                                   17,046        3,639    4.79

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

 

从主要缓冲池的指标上来看,没有明显的问题。而从主要等待事件上看,主要都集中在和IO相关的项目上。看样子IO系统的写性能指标并不好。另外REDO LOG相关的等待事件占整个等待事件的21%,REDO LOG看样子存在优化的可能。

Statspack报告上看,每个小时的redo buffer allocation retries483次,目前的LOG BUFFER1M,加大LOG BUFFER可以减少这方面的等待。而目前共有16个日志组,每个日志组1个文件,每个文���60M。从日志切换情况来看,今天业务高峰期平均1分钟多一点切换一次日志。由于无法查看去年中秋节期间的数据,因此我只能推算,如果业务量加大10倍,那么每隔56秒钟就会产生一次日志切换,这样对系统的性能就会产生较大的影响。看样子,加大LOG BUFFER,增加REDO LOG文件的大小,性能提升10-15%还是很有可能的。

从今天的STATSPACK报告中,我们没有看到明显的BUFFER BUSY WAITS,一个小时也只有几百个BUFFER BUSY WAITS等待。通过测算,现在每个系统每秒的话单量不到300条,而每个插入的批次是800条,因此多个插入进程同时插入数据的机会较少,热块冲突不会很明显,而如果业务量增加10倍,那么热块冲突可能会变得很严重。目前南坪系统的数据库是8.1.7.4,人和系统的数据库是9.2.0.5。两套系统都没有使用自动段空间管理(ASSM)。通过检查,发现freelists参数都是缺省的1,这对于并发插入操作会有较大的影响。加大freelists参数,不仅仅可以减少并发插入时分配空块的等待,而且不同的插入进程会使用不同的freelists,因此不同的插入进程不会往同一个数据块中插入数据,从而避免不同的插入进程之间在业务高峰期间可能出现的热块争用。根据以往的经验,如果freelists设置合理,减少了这方面争用可能带来的性能提升可能达到5%-10%

目前核心表SM_HISTABLE是每天一张独立的表,每张表都是分区表,表中设置了一个专门的分区字段ID_HINT,进行范围分区,这个字段的值是循环使用的,到达10000000后自动回绕为1,分区脚本如下:

PARTITION BY RANGE (ID_HINT)

( 

  PARTITION PART_01 VALUES LESS THAN (1000000)

    NOLOGGING

    TABLESPACE CQYDSMSC_CENTER1

    PCTUSED    40

    PCTFREE    10

    INITRANS   1

    MAXTRANS   255

    STORAGE    (

                INITIAL          504K

                MINEXTENTS       1

                MAXEXTENTS       2147483645

                FREELISTS        1

                FREELIST GROUPS  1

                BUFFER_POOL      DEFAULT

               ), 

 

这种分区模式对于减少热块冲突帮助不大,不过如果调整了FREELISTS参数后,热块冲突的严重程度也会下降,对性能的影响也不会特别大。不过如果能把不同的分区放到不同的磁盘组上,那么使用HASH分区比范围分区在IO负载均衡方面的作用就更为明显了。于是我马上要来了系统底层存储的设计文档。南坪系统的底层设计了多个磁盘组:

#1  磁盘数:4X36.4   RAID类型:RAID10   对应PVhdisk2   对应VGDATA1VG

#2  磁盘数:6X36.4   RAID类型:RAID10   对应PVhdisk3   对应VGDATA2VG

#3  磁盘数:6X36.4   RAID类型:RAID10   对应PVhdisk4   对应VGDATA3VG

#4  磁盘数:16X36.4  RAID类型:RAID10   对应PVhdisk5   对应VGDATA4VG

#5  磁盘数:16X36.4  RAID类型:RAID10   对应PVhdisk8   对应VGDATA5VG

人和系统使用的是72G的磁盘,20块磁盘做成RAID 10,VG划分的时候做了stripe。从底层存储的设计上,对于南坪系统,如果使用HASH分区,可以将IO均匀的分配到5个盘组上,从而避免业务高峰期的IO热点。这方面的调整,根据以往的经验,可以提升性能10-15%

ID_HINT列上,使用了一个SEQUENCEID_SEQ,我检查了一下,这个SEQUENCECACHE设置为1000,根据我以往的经验,设置为1000一般来说也够用了,不过下一步我们还需要进一步测试一下,CACHE设置的更高是否可能带来性能的提升。

从刚才的分析来看,我们目前可以达到的系统性能提升为1-1-0.1*1-0.05*1-0.1=23%。这是一个比较保守的数字,实际上提升的比例可能更高。因此通过这些方面的调整达到预定的优化目标还是很有可能的。

实际上,对于INSERT操作来说,使用BULK INSERT操作可以大幅度提升插入的性能,不过使用BULK INSERT,对于华为来说,应用需要做较大的修改,昨天在讨论方案的时候,华为方面感到如果要在短时间内完成修改,有些为难。而用户也希望把BULK INSERT作为最后的解决方案,可以作为建议提出来,华为在下一步的优化工作中进行修改,但是不列入本次优化的范围。

今天是在分析问题的过程中度过的,因此感觉过得很快。快下班的时候,老方打电话过来,说今天在武汉飞机晚点,刚刚到北京。一下飞机他就打电话询问情况。我说通过一天分析,感觉通过我们昨天考虑的几方面的优化,性能提升30%还是比较靠谱的。老方听后长舒了一口气,在飞机上他一直在考虑这个项目在售前阶段过于自信,没有做深入的分析,最终能不能让客户满意。

和老方通完电话,我给客户的余经理打了个电话,问她能不能安排一个业务不是很忙的时间,让我做一些针对性的实验,以便于确定优化方案。余经理说这是一个后台系统,我可以随便进行测试,只要不让话单积压过于严重,导致话单丢失就可以了。于是我连忙联系华为的维护工程师,和他讨论如何避免系统话单积压。华为的维护人员说他们本身就有一个实时监控系统,如果话单缓冲区占用率超过60%,就会产生告警,只要缓冲区保持在60%以下,风险就不是很大。于是我们约定好,明天白天我就开始进行一系列实验,一旦系统出现风险,他们马上通知我,我立即停止实验。

 

Permalink     No Comments

3月24日 简单任务 (1)令人意外的开始

到粉丝网上阅读:http://www.oraclefans.cn/forum/showtopic.jsp?rootid=17891

 

3月24日 简单任务 1)令人意外的开始

今天第一天到用户现场,我到香格里拉酒店和老方会合,老方在原厂,出差必须享受五星级标准,我就觉得400多块钱的酒店有点奢侈,住在旁边的迎宾馆。迎宾馆是原来的市委招待所,后来改造成了一个三星级酒店,虽然房间有点旧,不过十分干净。很大的院子,里面十分安静。最关键的是,一个市政府的朋友帮我打了个很不错的折扣,不到200块的价格是我选择这里的很重要的因素。

客户在开发区,从市区打车过去要40多分钟,在车上老方给我介绍了这个项目。老方是这个项目的负责人,前期也担任了售前的任务。听老方介绍,这是一个短信平台的优化项目,用户的优化目标是整体处理能力升25%以上,并没有对CPU资源、IO等提出明确的优化目标。听这个指标,好像优化目标并不高,老方也觉得这是个很简单的任务,只要花点心思优化几个SQL,就应该不难达到优化目标。在出租车上的这段时间,我们两个都很轻松,老方甚至考虑到不要过早结束项目的问题,因为客户出了一个不错的价格,如果我们过去三下两下就解决了问题,客户是不是会觉得钱花的有点冤枉。

上午是项目启动会,甲方的项目经理姓余,是个80后的美女,高挑的身材,说话甜甜的,听着比较舒服。从甲方对项目的期望来看,确实像老方所说,不算太高,比我们以前做的项目要相对简单一些。这是一个短信平台的系统,目前在平时是没有任何问题的。只有在中秋节和春节期间会出现性能问题,导致短信话单无法正常入库,严重的时候,为了确保短信系统能够正常运作,会出现应用软件被迫丢弃话单的情况。对于运营商来说,丢弃话单就意味着话费的流失,每年由于丢弃话单导致的话费流失达到数百万之巨。这还不是最大的问题,由于丢失的短信话单有可能是某个SP的业务短信,这就会导致SP业务收入方面的损失。为了确保业务高峰期不丢失短信话单,甲方希望通过本次优化能够将目前每秒最大入库短信量从1200条左右提高到1500条以上。其他方面的优化,能实现多少就算多少了,只要事务平均响应时间能够提升20%以上项目就可以验收了。

今天参加开工会的除了甲方外还有开发商华为的技术人员,华为方面对这次优化的态度十分友好,他们也希望我们能够对他们的系统的性能方面提出一些有益的建议,以便于改进他们的系统,他们表示只要我们提出的优化建议,一定在20个工作日内完成修改工作。

今天这个会只开了个把小时,就在友好的气氛中结束了。甲方的小余帮我们在旁边的ADC项目组的办公室里找到了两个空座位,然后是申请网禁,申请工作账号等工作。运营商在安全方面管的比较严,填写了数份表格,复印了身份证,甚至在安全责任书上按了指印,终于可以连到服务器上了。看看时间已经是中午1150了,于是我和老方两个先锁了电脑屏幕,到外面去找吃饭的地方。

老方对这个地方也不熟悉,我们两个转了半天,终于发现了一条比较热闹的街道,街道两边有几家饭店和一个不大不小的超市。老方是山西人,比较喜欢面食,于是我们两个在一家规模比较大的饺子馆吃了一顿饭。虽然是在西南,不过这家饺子馆还颇有北方的风味,两个人吃的都比较满意。回来的路上我顺便在超市里买了一个喝水的杯子,我这个人好喝茶,不过每次出差总是忘记带杯子。还好我喜欢喝绿茶,随便找个茶杯就可以泡茶了。于是每到一个地方,我总是先找超市买茶杯。

回到办公室的时候,已经快2点了。客户中午两点上班,办公室里不少人还躺在简易床上睡午觉。我先把杯子洗了洗,泡上一杯茶,然后登录到系统上去看了看。客户的这个短信平台分为两个区,分别负责半个城市的业务。这两个区根据机房的地点被命名为人和平台和南坪平台,服务器是IBM P系列服务器,操作系统是AIX 5.3。南坪平台是早期建设的平台,数据库是8.1.7.4的版本,人和平台是前几年扩容的平台,数据库版本是9.2.0.6

我首先查看了一下,两套系统的Statspack都是新装的,开了自动采样,估计这还是老方上个月来这里做售前的时候装的。我在南坪平台上把今天上午900-1000Statspack报告生成了一份打开看了看,每秒逻辑读和物理读的数量都很小,逻辑读只有五、六千。平均每秒的事务数也只有0.93个。想起刚才小余说过的,这套系统平时的时候负载很轻,CPU使用率都只有20%左右,IO也很闲。确实如她所说,现在是下午两点多,CPU的使用率只有10%多一点。我继续往下看Statspack报告,也没有发现什么开销较高的SQL。排名第一位的SQL是一条INSERT语句,是向一张SM_HISTABLE插入一条记录,根据表名判断,这就应该是那张短信话单表。这条语句在一个小时内执行了59万次,平均算下来每秒也不到200条。这条语句每次执行产生的逻辑读数量为14.2个。浏览了一遍Statspack报告,除了部分SQL没有使用绑定变量,应分析比例有点高之外,我也没有发现什么明显的问题。因为没有采集到业务高峰的数据,所以没有发现什么明显的问题也很正常,不过突然我发现了一个更为严重的问题,就是刚才我浏览TOP SQL这一节的时候,看到的单次执行开销最大的SQL也不过每次执行产生了不到2000个逻辑读。这说明系统中并无很多高开销的SQL,这样一来,早上我和老方商量的优化几个高开销SQL的计划就很难实施了。

我急忙把老方叫到门外,我们两个点上一支烟,然后我把刚才的发现告诉了他。老方也觉得有点意外,因为客户这次优化的目标并不高,老方认为达成优化目标没有任何问题,因此对于这套系统,他并没有像以往那样在售前阶段进行详细的分析。

我刚才发现,这套系统十分简单,除了那条开销最大的插入语句,只有少量的SELECT语句,而且大多数SELECT语句都是根据主键MSG_ID访问的,只有少量通过PHONE_NO查询的。在这样一套系统中做优化,想从SQL入手,估计难度较大,我们必须另辟蹊径。

如果不从SQL入手,我们该从哪里开始呢?这么简单的应用,通过操作系统和数据库参数方面的调整想要提升20-30%的性能,是不大现实的。调整应用的架构可能是最佳选择,不过这样的话,对应用调整过大,华为方面估计也会有很大的阻力。

讨论了半天,我和老方也没想出什么好的办法,于是我建议还是先找华为的开发人员了解一下应用的情况再说。从目前的情况看,只能先搞明白应用的关键瓶颈,再去想办法了。

经过了解,我才大体明白了这个系统中的关键问题。这是一个短信平台中的后台系统,用于存储和管理短信话单。来自短信平台的话单首先会存储在内存缓冲中,然后被后台进程批量写入数据库中。每个平台都配置了8个并发的写入进程,用于短信话单的写入操作,每次写入的批量为800条。他们也曾尝试过增加写入进程和增加写入批量的数量,不过效果不明显。SM_HISTABLE是一张分区表,首先他们设计了每个月一张话单表,表名为SM_HISTABLEyymm,每张表又按照每天一个分区,分为多个分区,分区主键是CREATED_DATE。每条话单都有一个唯一性的主键MSG_ID,话单插入后,后续处理中主要根据MSG_ID来查询话单数据,在做UPDATE数据的时候,一般都采用ROWID条件进行UPDATE,这方面也不会有性能问题。

从这种简单的应用系统来看,我们能够进行优化的点并不多。看样子我们需要认真的考虑一下这个特殊的应用,想想如何来完成这个现在看来很不简单的简单任务了。

 

 

Permalink     No Comments

3月23日 理解表的存储结构 (4)减少热块冲突的方法(下)

大家可以到粉丝网阅读本节:http://www.oraclefans.cn/forum/showtopic.jsp?rootid=17887

在实际的生产环境中,我们可能不总是那么幸运,我们肯定会碰到两方面的需求。一方面是热块冲突必须解决,另外一方面可能我们还存在一定的应用要对这些数据做范围扫描。在这种情况下,我们必须进行综合的评估,到底哪种需求是主要需求。如果我们优化范围扫描对系统更为有利,那么我们就必须放弃HASH分区;如果解决热块冲突更为重要,那么我们就必须牺牲范围扫描。实际上Oracle就是这样的,任何技术都是矛盾的,都是有缺陷的,否则我们就只需要记住一些准则,就可以成为大师了。Oracle的大师不是那么容易当的,因为在绝大多数情况下,没有永恒的准则。

HASH(HASH CLUSTER TABLE)簇表不是分区表,但是HASH簇表和普通的表(也就是术语是哪个说的堆表,HEAP TABLE)的存储方式是不同的,HASH簇表是簇表的一种,不过HASH簇表的组织方式是根据HASH值的,不同的数据块中存储不同HASH值的行。这样一来,哪怕同时插入的两条记录,也有可能因为某个字段的HASH值不同而存储在不同的数据块中。这样就可以最大限度的减少热块冲突。

Hash Cluster适合于对表的访问主要是等于条件的,很少做根据CLUSTER键值的范围扫描的情况。HASH CLUSTER TABLE除了优化热块冲突外,对于改善大表的SELECT操作也有一定的作用。使用下面的语句可以创建一个HASH簇表:

SQL> CREATE CLUSTER off_clu

  2    ( country        VARCHAR2(2),

  3      postcode   VARCHAR2(10) )

  4    SIZE             350

  5         TABLESPACE  case_large_data

  6         HASHKEYS        6000;

SQL> CREATE TABLE office

  2    ( office_cd   NUMBER(3),

  3      cost_ctr    NUMBER(3),

  4      country     VARCHAR2(2),

  5      postcode    VARCHAR2(10) )

  6    CLUSTER off_clu(country, postcode);

 

 

创建CLUSTER时的主要参数包括:

          HASHKEYS: KEY的数量

          HASH IS: 使用客户自定义的HASH函数

          SIZE: 预留的空间,用以存储相同HASHVALUE的不同的HASHKEY的值。如果HASHKEY的长度比较长,建议设置较大的值

除了HASH分区和HASH CLUSTER TABLE外,我们仍然有很多种方法可以用来减少热块冲突。加大PCTFREE是一种最常用和简单的方法,PCTFREE增加后,平均每个数据块中的记录数也就会减少,从而减少热块冲突的发生机会。

还有一种办法是使用较小的BLOCK SIZE,从9i开始,Oracle提供了一种新技术,就是用户表空间可以设置独立的BLOCK SIZE,而不需要和数据库的缺省BLOCK SIZE相同。这种技术为解决热块冲突提供了另外一种选择。不过使用不同的BLOCK SIZE这种十分优秀的技术在我们的实际应用环境中很少见到。这是因为开发厂商往往不了解这种技术,因此也很少有系统被这样设计。

好了,说了这么长一大篇,可能很多读者都昏昏欲睡了。关于表及相关存储参数的讨论到此先告一个段落,今后我们还会继续讨论这个永恒的话题。从明天开始,老白将通过一个实际的优化案例来重申表结构及参数的设置是如何的重要。

 

 

Permalink     No Comments

3月23日 理解表的存储结构 (4)减少热块冲突的方法(上)

请到粉丝网上阅读

http://www.oraclefans.cn/forum/showtopic.jsp?rootid=17841

 

 

实际上今天要讨论的内容在之前也零零星星讨论过,只是想通过今天的讨论,加深大家的印象。热块冲突是最为常见的现象,是DBA讨论最多的,不过也是DBA做的最少的部分。几乎所有的系统都存在热块冲突的问题,只是严重程度不同而已,一般系统的热块冲突对系统造成的影响都小于5%,因此绝大多数DBA对此采取了容忍的态度。实际上大多数的热块冲突都可以通过应用方面的优化来解决。解决热块冲突最为有效地方法除了修改SQL外,就是通过调整表的存储结构来解决。

为了探讨今天的话题,我们首先需要了解一下什么是热块冲突,热块冲突有哪些形式。首先要声明的是,本节的讨论时围绕着表这个话题的,解决热块冲突的方法有很多,我们会在今后的很多话题中再次讨论热块冲突,在这里我们主要讨论如何通过优化表的结构来减少热块冲突。

 

Permalink     No Comments

DBA日记第三部 像Oracle一样思考(5) 3月22日 理解表的存储结构 (3)那些逝去的老参数

大家也可以到粉丝网上阅读: http://www.oraclefans.cn/forum/showtopic.jsp?rootid=17562

3月22日 理解表的存储结构 3)那些逝去的老参数

有几个参数可能随着Oracle数据库版本的变化逐渐的逝去了,不过那些参数可能承载了很多老DBA的荣誉和苦泪。它们是pctusedfreelistsfreelist groups。随着自动段空间管理(ASSM)在9i的推出,现在新的数据库很少还在使用手工段空间管理(MSSM)。自动段空间管理和手工段空间管理最大的区别在于空闲块的管理上,自动段空间管理出现之前,Oracle的空闲块是通过FREELIST机制来管理的,表和索引在被创建的时候必须指定PCTFREEPCTUSEDFREELISTSFREELIST GROUPS 等参数。一个被格式化后的数据块被挂在FREELIST上,这样需要插入数据的前台进程可以在FREELIST上找到可以插入数据库的块,插入相关的数据,数据插入后,如果数据块中的空闲空间小于PCTFREE指定的比例,那么这个数据块就不能再次被插入数据了,这个数据块将从FREELIST上摘除,下次插入数据的时候,就会插入到其他的数据块中。这个数据块中如果有数据被删除后,空闲空间低于PCTUSED的指标,那么这个数据块将会再次被放到FREELIST上,这样这个数据块就可以再次插入数据了。有人可能会有疑问,一个比较“满”的数据块,如果删除了数据,那么不就马上可以被插入数据了吗?这个时候马上就放回FREELIST上不就行了,还用PCTUSED干什么呢?实际上Oracle的这个设计是十分优秀的,如果不设计PCTUSED这个参数,我们会碰到这样的情况,一个数据块刚刚插入数据后被从FREELIST中摘除,马上又因为删除数据被再次放入FREELIST,那么就可能会产生抖动,FREELIST的性能就会因为频繁重组而急剧下降。

同样的道理,在一个MSSM管理的段中,如果PCTFREEPCTUSED两个参数设置的不合理,那么这种抖动现象还将会出现。比如说我们设置PCTFREE30,设置PCTUSED65,如果行长度较大的话,如果一个数据块中刚刚插入一条记录后马上又删除了23条记录,这种情况下,这个数据块可能出现刚刚被从FREELIST上摘除,就马上被再次放入FREELIST的现象。从对MSSM管理空块的方法,我们可以看到设置合理的PCTUSED参数的重要性,如果PCTUSED参数设置过高,可能导致FREELIST的抖动,如果PCTUSED参数设置过低,可能导致数据块中存储的记录数过少,影响全表扫描操作的性能。

讨论完PCTUSED我们再来看看另外一个重要的段空间管理相关的参数FREELISTSFREELISTS是用于存放可插入数据的数据块的,FREELISTS存在于perment data segmenttemporary data segmentindex segmentrollback segment中,一个段中有几种类型的FREELISTS

l         MASTER FREELISTS

l         PROCESS FREELISTS

l         TRANSACTION FREELISTS

一个段中只有一个MASTER FREELISTS,这个FREELISTS在段创建的时候就创建了,段中的高水位以下的空闲块都挂在MASTER FREELISTS上。当某个前台进程需要插入数据的时候,就可以到MASTER FREELISTS上去查找空闲块,查找操作从FREELISTS的头部开始,直到好到可以插入数据的块为止。

明眼人看到这里马上就会看出问题来了,FREELISTS是一个串行的机构,如果有大量并发的会话需要插入数据的话,FREELISTS就会成为一个瓶颈。实际上ORACLE对此也早有对策,Oracle存在另外一种FreelistsProcess FreelistsProcess FreelistsMaster Freelists是一种主从协同机制,每个Process Freelists上链接了一组空闲块,这些空闲块是从Master Freelists上摘取的,前台进程要插入数据的时候,首先必须锁定一个Process Freelists,然后从这个Process Freelists上查找一个空闲的数据块用于插入数据。当Process FreeLists上也没有可用的空闲块的时候,会锁定Master Freelists,从中分配一组空闲块到Process Freelists上,然后再从Process Freelists上分配空闲的空间。听起来似乎很不错,OracleProcess Freelists的设计避免了Freelists成为并行插入操作的瓶颈。不过大家要注意的是,Oracle缺省的Freelists参数值是1,在Freelists参数为1的时候,段头中只有一个Master Freelists,而不存在我们希望的Process Freelists

不幸的是,绝大多数用户在使用MSSM的时候并没有设置Freelists参数,他们并没有留意到这种设置带来的负面影响。这种负面影响到底有多大呢?老白遇到过的一个最为极端的案例是一个移动公司的短信平台,这个平台每天需要插入的数据量在几千万条记录到几亿条记录,由于Oracle数据库使用的是8i,因此只能使用MSSM,在这个平台上我们进行了一个测试,在8个并发插入进程的情况下,如果FREELISTS设置为8,性能比FREELISTS设置为1 提高了5.7%。在一半的情况下,可能性能提升没有这么明显,不过如果Freelists参数设置不合理,造成1-5%的性能影响还是很可能的。

Freelists的机制设计的确实比较巧妙,似乎我们的所有问题都已经被解决了,不过细心的读者还会有一些疑问,那些已经标志为“满”的块怎么再次回到Freelists上呢?难道只要数据被删除了,块使用率低于PCTUSED,这个数据块就马上被放到Freelists上吗?这么做似乎也不太稳妥,一旦这个删除操作回滚了,那么岂不是又要再次把这个块从Freelists上摘除吗?实际上Oracle早就为这种操作设计了算法,这就是Transaction Freelists。一个段缺省有16Transaction Freelists,如果某个事物要修改这个段,首先会搜索Transaction Freelists,如果找到了这个会话的Transaction Freelists,那么就继续使用,否则这个会话就会继续找一个空闲的槽位,如果找不到空闲的槽位,这个会话就会试图去扩展一个新的Transaction Freelists,如果要扩展的时候,正好Segment Header中已经没有空闲空间了,那么这个会话就只能等待其他会话提交后腾出空闲的槽位。这种等待有点类似于我们前面说到的事务槽的等待,不过事务槽的等待产生于数据块中,而Transaction Freelists的等待是存在于Segment Header中。如果我们使用MSSM,并且Segment Header的等待较为严重的话,我们就应该检查检查是否是由于这个原因引起的等待。

当某个会话申请到Transaction Freelists后在某个数据块中删除了一些数据,这个数据块的使用率低于pctused参数后,这个数据块就被挂到Transaction Freelists上了,这个时候这个数据块可以被插入数据了,不过由于这个数据块只是在本会话的Transaction Freelists上,因此在这个事物提交之前,只有本事务可以在这个数据块中插入数据,只有等这个事物提交后,Transaction Freelists上的数据块才会被挂到Master Freelists的尾部,这个时候其他的会话也可以使用这些空闲块了。

是不是很巧妙的算法?Oracle通过这三种Freelists,十分好的解决了插入数据时的性能问题,将Freelists机制的瓶颈降到了最低。

对于MSSM来说,还有一个十分重要的参数Freelists Groups,这个参数一般来说被认为是OPS/RAC相关的参数,实际上这种认识也存在一定的片面性。不过我们还是先从这个参数在RAC环境下的应用说起。对于RAC系统来说,在MSSM环境下,Freelists的算法和单实例环境是十分类似的,了解CACHE FUSION机制的读者这个时候就会担心了,这种机制可能在RAC环境下产生严重的GLOBAL CACHE相关的性能问题。比如不同的实例上都会对某张表进行插入,那么如果INSTANCE 1上有一个会话在BLOCK 10中插入了一条记录,然后INSTANCE 2上有一个会话在BLOCK 10中插入了另外一条记录,INSTANCE 1再次在BLOCK 10中插入记录,这样的操作如果很多的话,那么就会成为一场灾难。这个数据块在两个节点中不停地来回传播,形成了GLOBAL BUFFER BUSY,这种数据块一旦躲起来,那么这个RAC系统的性能就会急剧恶化。

事实上,Oracle有足够的技术来避免这种悲剧的发生,Freelist Groups就是为解决这个问题而设计的。比如我们目前的RAC环境是一个2个实例的数据库,我们对某张表设置了Freelists Groups参数为2,那么这张表创建完毕后,在这张表的Segment Header中会有两个BLOCK,专门用于存储两组不同的Freelists(我们称之为Freelists Block),这种情况下,Segment Header Block里只有一个Super Master Freelists,上面挂载了这个Segment中的空闲块,而在每个Freelists Block中都有一个Master Freelists和若干个Process Freelists(由参数Freelists参数确定),在这两组Freelists上分别挂了一组空闲块,当某个实例上的会话需要插入数据的时候,会通过INSTANCE_IDPIDINSTANCE数量、Freelists Groups参数等因素来选择一组Freelists,从这组Freelists上选取空闲块。只要我们设置的Freelists Groups等于INSTANCE的数量,我们就可以确保每个实例都有自己独立的Freelists组,由于一个数据块只能挂载在一个Freelists上,这样就确保了不同实例插入数据时,不会选择不同的数据块(Freelists Groups大于Instance 数量一般也可以确保在大多数情况下不会出现不同实例共享相同的Freelist Groups,由于其选择算法较为复杂,我们这里不做详细的讨论)。

这种机制就很好的避免了我们刚刚所担心的问题,这是不是很美呢?而事实上不幸的是,我在十多年的优化工作中,还没有碰到一套系统为RAC环境设置了Freelist Groups参数,所有的系统都是用了缺省值,而Oracle的缺省值恰恰是每个段只有一个Freelists组。更为不幸的是,一旦段创建之后,Freelists Groups就是固定的,我们无法动态的扩展Freelist Groups,如果要扩展Freelists Groups,我们必须对这个段进行重建。因此在做数据字典设计的时候,开发人员应该事先为RAC环境设计好Freelist Groups参数,以避免今后扩展带来的麻烦。在考虑Freelist Groups参数的时候,我们不仅仅要考虑到RAC环境,而且要为今后RAC节点的扩展预留足够的Freelist Groups

看到这里,读者应该理解了Freelist GroupsRAC应用中的作用,而事实上,Freelist Groups不仅仅在RAC环境下有用,在一些极端条件下,这个参数在单实例环境中也能发挥作用。在一个插入并发量很大的环境中(比如有几十甚至上百个并发会话会对同一张表进行插入),我们可能经常会观察到这张表的segment header的等待,这种情况下,设置Freelist Groups大于1可以减少由于Freelist争用而导致的Segment Header的热块争用情况,多个Freelist Groups可以将多个对Freelists的并发操作分散在多个Freelists Block中,从而提高并发插入的性能。

我们在这一节中讨论了大量的FreelistsFreelist Groups参数在解决大并发量插入时的性能问题上的优点,这并不说明我们可以随便滥用这两个参数。这两个参数除了优点之外,也有一些缺点。多个Freelists可能导致数据块中的数据填充率变低,段的高水位推进过块,存储相同数量的记录,可能会占用更多的数据块。对于表扫描较为严重的应用来说,我们需要慎重考虑二者的正面和负面的影响,从而权衡使用这组参数。一般来说,在OLTP系统中,插入并发量很大的表,一般来说表中的数据量很大,应用访问这些表的数据的时候,大多数情况采用索引扫描,因此加大这组参数的副作用较小。而对于OLAP系统,我们切不可为了提高数据并发加载的性能而轻易加大这组参数,因为OLAP系统的数据经常需要进行全表扫描,这组参数的使用可能会影响全表扫描的性能。

谈了这么半天,可能有读者会说了,现在我们主流的数据库都已经是10g甚至11G了,从oracle 9i开始就支持自动段空间管理了,讨论手工段空间管理是不是已经没有多大意义了呢?事实上对于DBA来说了解一些MSSM的技术还是有意义的,一方面是我们很可能碰到8i的数据库,也可能碰到一些9i或者10g的数据库,它们是从8i升级上来的,可能很多表空间都是MSSM管理模式的。甚至我们可能为了规避某些BUG(比如说9iASSM表空间由于object reuse导致的ORA-600 [kcbget_xxx]ORA-600 [kcbnew_xxx]之类的BUG)而是用MSSM的表空间。甚至在某些极端的情况下,为了避开ASSMBITMAP带来的性能问题而反过头来是用MSSM(在绝大多数情况下ASSMBITMAP机制在性能上会优于Freelists管理模式,不过在某些特殊应用情况下,可能会反过来)。

通过上面的讨论,大家是否理解了MSSM下段参数的设置要点了呢?实际上,在具体的应用环境中,可能情况远比今天老白和大家一起探讨的要复杂的多,如何在纷繁的头绪中处理好这几个看似很简单,但是又很让人头痛的参数呢?这需要数据字典的设计者首先要对MSSM管理的机制十分了解,另外要对自己应用的特点十分了解,才能做出合理的选择。最后要提醒的是,当你无法做出合理的判断的时候,做个试验可能是比较明智的选择,到底哪种选择更好,应用系统说了算。

 

Permalink     1 Comment

3月18日 理解表的存储结构 (2)PCTFREE和行迁移

请到粉丝网阅读: http://www.oraclefans.cn/forum/showtopic.jsp?rootid=17544

由于本BLOG在文章篇幅较长的时候和插图方面显示不尽如人意,因此DBA日记将发布在粉丝网上,在本BLOG上仅发一个链接。不便之处,请大家原谅。

Permalink     No Comments

DBA日记第三部 理解Oracle的基本概念(1) 3月17日 理解表的基本概念(1)

请到粉丝网上阅读:http://www.oraclefans.cn/forum/showtopic.jsp?rootid=17464

 

Permalink     No Comments