虽然ORACLE的售前人员总是说RAC的扩展性是透明的,只要把应用分到不同的节点,就可以平滑的扩展系统能力了。而事实上,RAC的CACHE FUSION机制决定db cache,library cache等在RAC环境下都会由于CACHE FUSION而带来额外的开销。
在一个单实例环境中,如果我们要访问某个Cache buffer,我们只需要闩住相关的cache buffer chains,然后
去读取这个buffer就行了,而在RAC环境中,首先我们要闩住cache buffer chains,查看该buffer的当前版本是否在这个实例的db cache中,如果没有找到,那么就要发出消息询问其他的节点,是否cache在其他节点中存在,如果存在,那么就要从其他节点通过网络将这个buffer 传输过来。
在一个单实例的环境中,db cache的命中率 96% 和 97% 可能对系统影响不大,而在RAC环境中,db cache 命中率下降一个百分点,对于系统性能造成的总体影响要大上数倍。因此在设计RAC应用的时候,如何减少CACHE FUSION带来的负面影响是十分关键的。在一个RAC环境中,没有任何数据共享是不可能的,或多或少都会有数据共享,因此如何在实例之间共享数据是解决 RAC应用性能问题的关键。
在国内的应用设计方面,为了避免 CACHE FUSION使用最广的方法是应用隔离,也就是在RAC的不同节点上跑不同的应用,从而最大幅度的减少数据的共享。应用隔离确实是一种很好的RAC应用优 化方案,实施起来也比较简单,不需要十分专业的RAC优化技术。不过应用隔离虽然实施难度较小,也被使用比较广泛,但是应用隔离是RAC优化中最为低级的应用层次,在很多情况下,某个应用主要集中在某几张表的访问,这样想要做的应用隔离的难度较大,在这种情况下,需要对应用做更为细致的优化。就像老白处理的这个案例,在两个节点要跑完全相同的应用,这种情况下的优化,就需要从应用底层开始设计了。从总体上来说,减少CACHE FUSION带来的性能影响的方法有一下几个方面。
1、通过表分区来限制某个分区被某个实例使用。对于RAC应用来说,表分区是很好的减少热块争用的手段。首先通过表分区,使数据分散到数个segment中,其高水位推进,数据访问等都被分散了。因此在RAC环境中设计表分区的时候也要注意,表分区逐渐的选择确实能够起到分散数据分布的作用。如果我们有一张日志表,根据日志的日期进行分区,那么虽然我们做了分区,但是还是无法起到分散数据访问的作用,因为我们总是在最新的那个分区里插入和查询数据,其他分区很少会被访问到。
2、增加db cache的命中率。在一般的OLTP系统中,db cache命中率超过90%就不算太低了,如果超过95%那就说明db cache的命中率比较高了。但是在RAC环境中,db cache的命中率越低,CACHE FUSION带来的负面影响就越大,因此在RAC环境下,保持较高的db cache命中率对于系统总体性能大的影响十分巨大。
3、增加共享池的命中率。在RAC环境中,SQL的分析等操作要比单机环境昂贵的多,主要原因是分析等操作涉及大量的全局资源的协同。在RAC环境中减少硬分 析、软分析等也有十分重要的意义。因此保持较为充足的共享池资源,使用好的编程习惯,合理使用绑定变量等都有助于提高RAC环境下的系统性能。
4、加大sequence的cache,并使用noorder选项。在RAC中经常会遇到SQ锁等待,这是因为在RAC环境下,sequence也成为全局性的了,不同节点要生成序列号,就会产生对sequence资源的争用。而目前大多数系统中,sequence大多数被作为主键发生器来使用,使用的频率十分高,在RAC环境中, 需要设置较大的 sequence cache,否则会造成较为严重的争用,从而影响业务。
另外在业务允许的情况 下,sequence 尽可能使用noorder选项,从而减少sequence 产生器的负担。对于十分严重的sequence争用,甚至有用户使用UUID来替代sequence.UUID是一个37位字符串,通过生成机制来确保全局唯一性。这个特点也可以用来作为主键使用。在Oracle中,可以通过 select sys_guid() from dual来生成一个UUID,这个UUID是不带“-”的,所以只有32位。
关于enqueue的一个案例
平均事务响应时间在200毫秒左右,IO基本上很空闲,两个节点的CPU使用率比较低,基本稳定在30%左右。从主要等待事件上看,global cache cr request、db file sequential read和enqueue排在前三位。enqueue等待大概占整个等待事件的13%左右。我马上打开了enqueue的图表,发现排在第一位的 enqueue是SQ。通过脚本检查v$enqueue_stat:
SELECT eq_type,cum_wait_time
from v$enqueue_stat
order by 2;
从查询结果上来看,排在前三位的是SQ、US和TX。SQ排在第一位肯定是sequence存在较为严重的冲突。我做了一个statspack报告,从 Statspack报告中可以看出row cache objects相关的等待还是比较多的,从row cache的情况看,dc_sequence的等待和丢失率也比较高。于是我检查了一下sequence的参数,所有的sequence cache参数都是缺省的20。于是我让秦工尽快联系开发人员,重建所有的sequence,将sequence 的cache参数都加大为1000。
随着系统负载的增加,CPU的使用率也提高了40%以上,global cache相关的等待事件和enqueue等待也比刚才多了一些。突然整个系统出现了异常,平均事务响应时间出现了一个突变,提高了一倍多,好像出现了短 暂的HANG住现象。我看了一下OEM的健康性图标,发现logon的指标不正常得高,在两个节点上平均每秒logon的数量达到了30多。我问秦工是不是系统有短连接的应用。秦工说他们的系统中绝大多数都是C/S结构的,平均每秒logon的数量一直都挺高的。
看样子我刚才又忽略了一个问 题,在RAC环境下,如果logon较多的话,需要加大一个sequence的cache,刚才开发厂商已经加大了所有应用的sequence的cache,但是sys.audseq$的cache却忘记调整了,检查了一下,确实sys.audseq$的cache是缺省的20.在RAC环境下, 如果logon较为频繁的话,这个sequence是必须调整的。我甚至碰到过由于logon的密度比较高,导致整个数据库HANG住的现象。于是我建议等会调整的时候,把sys.audseq$的cache调整为5000
关于sequence cache
1、Create Sequence
你首先要有CREATE SEQUENCE或者CREATE ANY SEQUENCE权限,
CREATE SEQUENCE emp_sequence
INCREMENT BY 1 -- 每次加几个
START WITH 1 -- 从1开始计数
NOMAXVALUE -- 不设置最大值
NOCYCLE -- 一直累加,不循环
CACHE 10;
5、使用只读表空间。只读表空间在RAC中时十分有用的,在CACHE FUSION下,如果某个表空间是只读的,那么表空间中数据的访问只需要本地操作就可行了,不需要RAC间协同。实际上,很多数据库中的数据,在某个时间段里都会有大量的只读数据,这些数据如果访问十分频繁,那么在RAC下就会对GES和GCS产生很严重的影响。实际上,如果我们在设计应用系统的时候就充分考虑了RAC的问题,完全可以把可能只读访问或者在某个时间段内只读访问的数据放在相同的表空间中,这些数据可以根据业务需要设置为读写或者只读。比如,老白曾经给一个客户做过优化,他们系统中的历史数据访问十分频繁,老白建议他们把历史数据存放在单独的表空间中,平时设置为READ ONLY,只有做数据归档的时候才改回READ WRITE。通过这个简单的优化动作,减少了大量的全局访问,改善了RAC的性能。
6、减少大表的全表扫描。全表扫描特别是大表的全表扫描,对 db cache的影响十分巨大,由于全表扫描会占用大量的db cache,这样就会把很多DATABLOCK 从 db cache中挤出去,而且大表的全表扫描会带来大量的物理读,在RAC环境中,物理读的开销远大于单机环境。
7、限制并行查询在实例范围内,不要再RAC实例之间做并行查询。实际上,绝大多数的并行查询并不需要跨实例运行,而Oracle 缺省情况下并行查询是跨实例的,所以很多客户都吃过这个苦头,跨实例做并行查询主要是解决单机CPU能力不足的问题,但是跨实例并行查询会引起GCS方面的问题。目前国内用户系统的单机CPU能力一般都比较强