Oracle Instance
以前也学习过oracle 逻辑结构的知识,但用的不多好多都是有点概念,最近做到一个跨instance工作流,所有抽点时间温习了一下相关知识,把网上看到的感觉讲的还比较明了,全面的文章汇总一下
instance = 内存结构(SGA,system global area) +后台进程
内存结构 = 共享池+数据高速缓存+重做日志缓冲区+其他
后台进程 = DBWn(database writer n个)+LGWR(log writers)+SMON(system monniter)+PMON(progress moniter)+CKPT(checkpoint)+……
PGA(Program Global Area)= 服务器进程+后台进程
下面分别介绍各部分:
共享池
1、共享池用于存放最近执行的SQL语句和数据字典信息,其尺寸由初始化参数SHARED_POOL_SIZE定义。共享池主要由库高速缓存和数据字典高速缓存两部分组成。
库高速缓存用于存放最近执行的SQL语句信息,包括SQL语句文本,解析代码值及其执行计划。执行计划是SQL语句的内部操作步骤。例如:当执行select name from students where grade=90时,如果grade列上不存在索引,执行计划将采用全表扫描;如果有索引,执行计划将使用索引和ROWID定位数据。
如上图所示,库高速缓存包含许多上下文区,每个上下文区都有相应的SQL语句执行计划,这些上下文区又被称为共享游标。当客户端运行SQL语句时,服务器进程首先检查共享游标是否存在,若存在则按照执行计划直接执行即可;若不存在则生成SQL语句执行计划,并将执行计划存放到相应的上下文区中,然后执行SQL语句。共享游标降低了SQL语句解析的次数,提高了执行性能。
另外,语句1和语句2由于文本不同,所以占用不同的上下文区,而对于文本完全相同的语句1来说可以共享上下文区。所谓的文本完全相同指的是:语句文本相同;大小写和长度相同;赋值变量相同。
数据字典高速缓存用于存放数据字典的信息,包括表、列的定义以及权限信息。
最后,库高速缓存和数据字典高速缓存的尺寸是可以变化的。动态改变共享池的尺寸:alter system set shared_pool_size = 60M;
数据高速缓存
数据高速缓存用于存放最近访问的数据块信息,它由许多小缓冲区组成。
数据高速缓存采用LRU(least recently used)算法管理数据高速缓存。
数据高速缓存的结构:分为三部分,分别是脏缓冲区、空闲缓冲区、忙缓冲区。脏缓冲区是指内容与相应数据块不一致的缓冲区;空闲缓冲区是指内容与相应数据块一致或者不包含数据的缓冲区;忙缓冲区是指服务进程正在存取的缓冲区。
重做日志缓冲区
重做日志缓冲区用于记载instance的变化。
后台进程
后台进程是指oracle server隐含执行的进程。启动instance时不仅会分配SGA,还会启动后台进程;关闭intance时不仅释放SGA所占的内存空间,而且会释放后台进程所占用的CPU和内存资源。下面介绍一下常用的后台进程:
SMON(System Monitor):用于执行intance恢复、合并空间碎片并释放临时段。
PMON(Process Monitor)用于监视服务器进程的执行,并且在服务器进程失败时清除该服务器进程。
--================================================================================
Oracle内存结构
Oracle内存结构主要可以分共享内存区与非共享内存区,共享内存区主要由SGA(System Global Area)组成,非共享内存区主要由PGA(Program Global Area)组成(见图1-1)。
从图1-1中可以看到,Oracle共享内存区主要包括数据缓冲区(Data buffer)、共享池(Shared pool)及一些其他的结构。而PGA则主要包括会话的一些信息及排序区,Hash join区域等,下面分别介绍这些内容。
SGA
系统全局区(SGA,System Global Area)其实是一块巨大的共享内存区域,包含了Oracle的数据缓冲及众多的控制结构。这里的数据可以被Oracle的各个进程共用,如果有互斥的操作,如锁定一个内存对象,则需要通过Latch与Enqueue来控制。
每个Oracle实例(instance)只能启动一个SGA,除非通过RAC等一些特殊的全局管理方式,否则不同的实例只能访问自己的SGA区域。通过如下的方式就可以查看SGA大小:
10gR2 Piner>show sga Total System Global Area 8877225568 bytes Fixed Size 755296 bytes Variable Size 486539264 bytes Database Buffers 8388608000 bytes Redo Buffers 1323008 bytes
以上结果是一个典型的OLTP环境中的SGA的分配情况,我们可以看到四个大的部分。
Fixed Size,包括了一些数据库与实例的控制信息、状态信息、字典信息等,启动的时候就固定在SGA中,而且不会改变。
Variable Size,包含了shared pool、large pool、java pool、streams pool、游标区和其他结构等,合计450MB左右。
Database buffers,有时候也叫Data buffer,它是数据库中数据块缓冲的地方,数据块在内存中就缓存在这里。所以,在OLTP环境中,Data buffer是SGA中最大的缓冲区,是数据库性能高低的关键所在。
Redo buffers,它是为了加快日志写进程的速度而设立的缓冲区,在一般OLTP环境中,因为提交很频繁,所以一般不会很大。
SGA的大小信息也可以从v$sga中获得,与show sga的结果一样。另外,v$sgastat记录了SGA的一些统计信息,v$sga_dynamic_components则保存了SGA中可以动态调整的区域的一些动态或者手工调整记录。
下面将介绍SGA中最重要的两个对象,共享池(Shared pool)与数据缓冲区(Database buffer),其他的池,如Streams pool,在介绍Streams的章节时再另外介绍。
共享池(Shared pool)
共享池是SGA中非常关键的内存片段,特别是在性能和可伸缩性上。由初始化参数shared_pool_size决定其大小,在Oracle 10g以后,Oracle可以自动管理大小。
在典型的OLTP高可用环境中,一个太小的共享池会扼杀性能,导致出现Ora-04031错误,使系统停止运行。但是太大的共享池也将消耗大量的CPU来管理。在数据仓库环境中,因为并发进程的需要,可能会分配比较大的共享池。
提醒:在实际的高可用环境中,有很大的一部分故障就是因为共享池的原因而导致系统停顿甚至宕机的,如Latch争用、Ora-0431错误、Library cache争用与等待。
不正确地使用共享池只会带来灾难性的后果,所以,必须先要了解共享池的作用。共享池又可以分为SQL语句缓冲区(Library Cache)、数据字典缓冲区(Data Dictionary Cache)及一些控制结构。而数据字典缓冲区与控制结构是用户无法直接控制的,所以,真正与用户有关的其实就是SQL语句缓冲区。
当一个用户第一次提交一个SQL语句,Oracle会将这句SQL进行硬分析(Hard parse),这个过程类似于程序编译,会耗费相对较多的时间,因为它要分析语句的语法与语义,获得最佳的执行计划(Sql plan),并在内存中分配一定的空间来保存该语句与对应的执行计划等信息。
因为Oracle总是保存语句的执行信息,当数据库第二次或者多次执行该SQL时,Oracle自动跳过这个硬分析过程,变为软分析(Soft parse)或快速软分析(Fast soft parse),从而减少了系统分析的时间,减少CPU与Latch的消耗。
注意:Oracle中只有完全相同的语句,包括大小写、空格、换行都要求一样,才能重复使用以前的分析结果与执行计划。
图1-2是一个完整的SQL语句的分析过程:
所以,如果大量的,频繁访问的SQL语句都不采用绑定(Bind)变量,Oracle为了做SQL的硬分析,Shared pool latch将变得严重争用与等待,同样也会耗费大量的CPU,直到机器的资源耗尽。另外,因为Oracle会从共享池中分配空间来保存刚做完硬分析的SQL语句,也将耗费大量的内存空间,而且,这些被浪费的空间无法被重用。
对于编程者来说,要尽量提高语句的重用率,减少语句的分析时间,一个设计很差的应用程序可以毁掉整个数据库。为了避免出现这样的问题,Oracle从9i开始,还引进了一个新的参数cursor_sharing,不过,因为该参数总是带来很多其他问题,如bug,所以,在现有的数据库版本上,还是不建议在高可用的生产环境中使用。
共享池采用LRU算法来决定共享池中的对象是否继续保存,因为其空间毕竟有限,不可能无限保存所有的信息。所以,假设一个语句已经被执行过了,如果长时间没有被使用,重新执行的时候,也可能面临重新分析,如果真的执行次数如此之少,就不是高可用环境需要关心的了。
对于间歇性访问的比较大的对象,如自定义的过程与包,如果不想在运行过程中被系统自动交换出去,可以调用DBMS_SHARED_POOL.KEEP存储过程将该过程或包pin在共享池中,以减少重新载入的巨大代价。
我们可以通过如下命令手工清除共享池的内容,除了有指导的特殊情况下外,该命令不建议在高可用的生产环境上运行。
SQL> alter system flush shared_pool;
与共享池相关的视图很多,这里介绍几个可能在高可用环境中经常需要用到的视图。
v$sqlarea,每条记录都显示了一个SQL语句的详细统计信息,包括历史以来的执行次数、物理读、逻辑读、耗费时间等非常多的重要信息。
v$sqltext_with_newlines,因为v$sqlarea只是记录了一个语句或者是一个游标的前1000个字符,如果是比较大的SQL语句,则不能在v$sqlarea中完全显示。如果通过这个视图,可以获得一个SQL语句的详细信息。在这个视图中,一个SQL语句分为多行保存,通过hash_value来标示语句,通过piece来排序。
v$sql_plan视图保存了被执行的SQL语句的执行计划,可以通过特定的脚本获得以前执行过的语句的执行计划,在本书的后面章节有这样的讨论。不过,该视图在9i的早些版本,如9206以前,存在一些bug,查询该视图,可能会出现600错误,甚至导致数据库崩溃。
v$shared_pool_advice,这个视图会对Oracle的共享池做一些预测,范围可能在当前值的50%~200%之间,优化者可以根据视图显示的信息做优化判断,如重新调整共享池大小。其中的字段SHARED_POOL_SIZE_FACTOR说明了预测的共享池大小与现在大小的比例。
对于并发很多,而且访问频繁的高可用环境,需要避免如下的一些情况,在本书的后面将有详细的案例分析来说明其原因与避免方法:
共享池不够或bug导致的0431错误,该错误可能导致系统无法访问。
分析数据改变导致执行计划改变,错误的执行计划可能导致系统无法运行。
增加了新的对象,如索引,引起执行计划改变而导致系统无法运行。
修改表导致依赖的存储过程或者是包失效,而无法自动编译成功,导致系统崩溃。
错误的操作方法导致严重的Latch争用与等待。
数据缓冲区(Data buffer)
数据缓冲区(Data buffer)
数据缓冲区(Data buffer)是Oracle中用于数据块缓冲的区域,数据库常规情况下读写(非直接读写)数据块,Undo块等,都会经过这个缓冲区,并适当地保存在缓冲区。如果下一次请求操作同样的块,则不需要从磁盘获得,大大提高了系统的响应速度。
数据缓冲区虽然不像共享池那样容易导致系统故障,但是,它却是影响OLTP系统性能的关键,因为它的Cache技术可以很大程度地避免磁盘寻道,直接从Data buffer中获得。所以,Oracle把从Data buffer获得数据块叫Cache hit,把从磁盘获得数据块叫Cache miss,它们的比率就是我们常说的Data buffer命中率。
在一个典型的OLTP的环境中,或者对事务型以及小查询型的数据库来说,更高的命中率意味着更快的响应速度,所以命中率一般要求在95%以上。大的Data buffer对提高系统的性能有巨大的好处,因为Data buffer比较大,缓冲的数据块也就比较多,命中率也就更高。
但是在典型的OLAP环境中,大的Data buffer则不一定是必要的,因为OLAP的查询基本是要求从磁盘返回,而且以直接读写居多,直接读写是不经过数据缓冲区的,使得命中率失去意义。所以在OLAP环境中,需要考虑用更多的磁盘驱动器,OLAP的速度取决于硬盘的多少与系统的带宽。
数据缓冲区中的块基本上在两个不同的列表中管理。一个是块的"脏"表(Dirty List),表示被用户修改过的数据块,采用检查点队列(checkpoint queue)来管理这些脏的数据块,必要的时候通过数据库写进程(DBWR)来写入这些脏块;另外一个队列是不脏的块的列表(LRU List),比如通过Select从磁盘获得的数据块。一般的情况下,Oracle使用最近最少使用(Least Recently Used,LRU)算法来管理这些队列,但是,从8i开始,另外还增加了Touch的概念,不仅仅是纯粹的LRU算法。
块缓冲区高速缓存又可以细分为以下三个部分Default pool、Keep pool、Recycle pool,在9i以前,它们对应的是db_block_buffers、buffer_pool_keep、buffer_pool_recycle三个参数,分别表示每个缓冲区块的个数。从9i开始,又重新引入了三个新的参数:db_cache_size、db_keep_cache_ size、db_recycle_cache_size,分别表示该缓冲区的字节大小。
从9i开始,Oracle支持创建不同块尺寸的表空间,这个新的特性同时也解决了在不同块大小的数据库之间传输表空间的问题,并且可以为不同块尺寸的数据块指定不同大小的数据缓冲区。不同块尺寸的数据缓冲区的大小就由相应参数db_nk_cache_szie来指定,其中n可以是2,4,8,16或32。如创建了一个大小为2K的非标准尺寸的表空间,就可以指定db_2k_cache_size为这个表空间指定缓存区的大小。
注意:db_block_buffers与db_cache_size这两种不同类型的参数,不能同时设置。另外, db_nk_cache_size不能设置默认标准块大小的缓冲区,如默认块大小为8K,则不能设置参数db_8k_cache_size。
正确使用Default pool、Keep pool、Recycle pool也可以提高系统的性能,如把一个访问很频繁的表或索引放置在适当大小的Keep pool中,可以减少物理读,提升IO性能。是否决定使用这个功能,要看系统的具体情况,如参考Statspack中的Segment统计信息,关注其中的物理读部分,分析Top物理读的对象,如果有些对象的确不大,但是物理读又很大,就可以考虑缓冲分离。
视图v$db_cache_advice与共享池的v$shared_pool_advice一样,由Oracle自动根据一些数学模型算法,收集信息后产生的一系列建议值,可以作为调整Data buffer大小的参考。
v$bh与x$bh
v$bh在研究与查询Data buffer使用上,是一个非常不错的视图,非常详细地记录了数据块在数据缓冲区内的使用情况,一条记录对应一个Block的详细信息。
如果通过如下语句来查询v$bh的来源,可以看到这个视图来源于基表x$bh与x$le,但是,主要数据与字段都是来源于x$bh的。
SQL>select * from v$fixed_view_definition t where t.view_name='GV$BH'不同的是,x$bh包含了更多的信息,如touch count信息,在Oracle 8i以上作为LRU算法的一个重要的参考信息,表示了一个块的热点程度。touch count信息对应到x$bh的tch字段,而段的data_object_id信息对应到x$bh的obj,或者是v$bh的objd。有了这些基本信息,其实就很好确认热点块在哪里了。
不同的是,x$bh包含了更多的信息,如touch count信息,在Oracle 8i以上作为LRU算法的一个重要的参考信息,表示了一个块的热点程度。touch count信息对应到x$bh的tch字段,而段的data_object_id信息对应到x$bh的obj,或者是v$bh的objd。有了这些基本信息,其实就很好确认热点块在哪里了。