深度对比Oracle与SQL Server – 第二部分

本译文采用知识共享署名-非商业性使用-相同方式共享 3.0 Unported许可协议发布,转载请保留此信息
译者:马齿苋 | 链接:http://www.dbabeta.com/2010/oracle-sql-server-comparison-ii.html
作者:Sadequl Hussain | 原文:http://www.sql-server-performance.com/articles/dba/oracle_sql_server_comparison_ii_p1.aspx


数据库实例结构

当Oracle实例启动之后,所看到的就是在服务器内存上的一个个不同内存块加上产生的与这些内存交互的后台进程。 Oracle文档将这些内存结构和进程收的很详细。

由Oracle实例所占用的内存块成为SGA(System Global Area),它的大小可以通过调整 Oracle初始化参数(initialisation parameter)进行修改,在SGA里边至少会创建3个不同的区域,它们分别是:

数据块缓存区(Database Buffer Cache)
  这里缓存的是数据块。和SQL Server一样,用户不会直接的访问数据文件上的数据:当读取数据时,相关的数据块会从数据文件中拷贝到内存中;修改数据时也是修改内存中的数据,然后再由单独的进程将数据缓存区中被修改的数据写入到数据库中。
重做日志缓存(Redo Log Buffer)
  SGA中的这个区域连续的记录着数据缓存区数据修改的记录,重做日志缓存中的内容会被写入到在线日志文件中去。
共享池(Shared Pool)
  SGA中有一大块的内存用作共享池,共享池等价于SQL Server中的执行缓存(Procedure Cache)。它的主要作用就是缓存数据库中最近执行过的SQL语句。共享池由下面的模块组成:

  数据字典缓存(Dictionary Cache)
  数据字典缓存缓存了Oracle最近使用的数据字典信息。
  库缓存(Library Cache)
  这个区域包含了最近执行的SQL和PL/SQL语句和对应的执行计划。SQL区还能进一步分成共享区和私有区:共享SQL区保存的语句可以由多个用户使用而私有区保存的则是跟各个连接对应的绑定变量的信息。PL/SQL是Oracle对于行业标准SQL的程序扩展,当PL/SQL程序执行时,它的代码会拷贝到库缓存中的共享PL/SQL区中。除了缓存执行代码和执行计划外,库缓存还包含锁、阀以及字符集等信息。

根据你所使用的组件的不一样,Oracle的SGA中也可能存在其它一些可选的内存区域。在Oracle中使用Java应用时要使用到Java池 (Java Pool),Oracle内置的备份恢复工具RMAN(Recovery Manager)要用到大池(Large Pool),当使用Oracle高级队列机制时要用到流池(Steams Pool)。

上面介绍的各个内存区域都是属于SGA的一部分,它们是通过一序列的进程 (process)来和数据库进行交互。下面就开始介绍包括用户进程和服务器进程在内的Oracle进程了。

当用户或者是应用连接到Oracle数据库时就会产生一个用户进程(user process)。在一个两层的系统架构中,用户进程存在于客户端机器上;而在一个三层的系统架构中,用户进程由中间层产生。一旦用户进程连接到Oracle监听服务 (Listener Service)时,监听器就会为这个用户会话产生一个服务器进程(server process)。再说详细点就是,Oracle监听器是一个负责为Oracle处理近来连接的网络组件,监听器本身是一个单独的进程,负责监视着从客户端过来的连接请求,如果监听器没有运行的话,数据库也就无法连接了。一旦连接建立,由监听器产生的服务器进程就开始接手处理用户操作数据库的请求了。大多情况下每个用户连接会产生一个独立的服务器进程,不过Oracle也能配置成一个运行着多个预先创建好的服务器进程池的模式,此时用户进程连接到数据库实例之后将会直接从进程池中分配一个进程。

任何时候Oracle实例中都会有多个后台进程在运行着,不过,有5个必须要有的。

写数据库(Database Writer)进程或称DBWn负责将数据缓存区中被修改的数据库写入到数据文件中。为提高系统系能Oracle可以最多创建20个这样的进程,DBWn中的n代表着一个单独的进程:n取值范围是0 到9和a到j。

在DBWn进程将脏块写入到数据文件的同时,一个叫日志写入(Log Writer, LGWR)的进程也在降日志缓存中的日志写入到在线日志中去。LGWR执行的要比DBWn进程频繁得多,主要原因有两点:首先是要尽快的将事务信息写入到磁盘当中,以保证当意外宕机等事件发生时数据库能正常恢复,在一个就是日志缓存中的日志信息记录的不是实际的变化的数据,但是却是能够反应数据的变化——因此相比之下也就要小得多了。

检查点(Checkpoint, CKPT)进程负责定期的将SGA中的内容同步到数据库中。当执行检查点时,它会调用DBWn进程将所有的脏块写入到数据文件中,同时将重做日志缓存中的内容写入到在线日志文件中,然后更新数据文件头和控制文件的相关信息。

如果某个访问数据库的进程意外的死掉的话,那进程监视器(Process Monitor, PMON)就会在后台悄悄的清理掉这个死掉的进程,所进行的操作包括释放相应的锁、回收分配的资源已经将这个进程从活动进程中清理出去。PMON进程有点像一个UNIX的 daemon进程:它会定期的唤醒以检查看是否有清理工作要做。在必要的时候也能启动一个别的进程或被其他的后台进程使唤。

在一个实例崩溃之后重启时,Oracle会自动的调用系统监视器(System Monitor, SMON)进程,SMON会利用在线日志文件来完成崩溃恢复操作。

归档进程(Archiver, ARCn)是Oracle实例中可选的几种进程之一,这里的n和之前说的一样都是代表着单个的进程。只有在数据库开启了归档模式(archive log mode)之后才会启动ARCn进程,在在线日志中的内容满了之后,在Oracle覆盖这些记录之前ARCn进程会将这些内存保存到磁盘上,这些文件称为归档日志 (archive log),这样的话在线日志内容就不会丢掉了。

下图显示的是Oracle实例结构的一个简化版。

最后,你可以通过下面语句来查看SGA各个组件的情况:
1 SELECT * FROM V$SGAINFO

下面的命令用来查看分配给Oracle实例的总内存数:
1 SHOW PARAMETER SGA_MAX_SIZE;

要查看一个实例上运行的Oracle进程情况可以使用下面的语句:
1 SELECT NAME, DESCRIPTION FROM V$BGPROCESS ORDER BY NAME

在一个运行着的Microsoft SQL Server实例上,同样维护者一些内部的内存结构和后台进程,不过不像Oracle那样,SQL Server并没有公开的详细解释内部工作和架构的文档。

自2005版以后,SQL Server引入了一个叫SQLOS(SQL Operating System)的东西,不过跟名字所显示的不一样的是,这个东西既不是一个操作系统,也不是对于OS API的一个封装。它并不是一个通向非Windows平台的桥梁或是一个用于框架开发的类库。

SQLOS是一套用于通过优化与Windows系统接口而为SQL Server存储和数据库引擎提供关键服务的软件,Windows是一个通用的OS:它并没有为SQL Server而做特别的优化。SQLOS则担当替代Windows而为SQL Server和管理核心系统服务的,SQLOS提供的服务包括:

  * 内存管理
  * 资源管理
  * 检测和管理死锁
  * 异常处理
  * CLR组件托管
  * 包括专用管理连接(Dedicated Administrator Connection, DAC)和动态管理视图(Dynamic Management Views, DMV)在内的诊断功能
  * 调度管理(马上开讲)

Windows下的应用程序都是运行在独立的、受保护的内存空间上,这些内存空间被称为虚拟地址空间(Virtual Address Spaces, VAS)。因为每个应用的VAS都是不一样的,因此一个应用是不能往另外一个应用的地址空间做写入操作的,这样保证了程序不会因为严重的违规访问而崩溃。分配给应用的VAS可能来自于物理内存或者是页面文件,又或者是两者都有。页面文件 (paging file)又称交换文件(swap file)是一个被Windows内存管理器用来存放那些无法放进物理内存的数据的,这些数据被写入到交换文件中,在应用需要的时候被读取出来。当中所涉及到的虚拟内存地址与物理内存地址之间的转换(在RAM和页面文件中)时由Windows内存管理器完成。从应用的角度来说,它并不关心它的内存究竟是存在什么地方的。对32位的Windows系统,OS所能寻址的最大的VAS地址空间是4GB,默认情况下这当中的2G供操作系统内核使用,剩下的2G提供给像SQL Server之类的应用使用。不过通过修改boot.ini中的开关(3G开关)设置可以让Windows只给自己分配1G而剩下3G给应用使用。要想在32位的机器上使用超过4GB内存的话,SQL Server可以通过地址窗口化扩展插件 (Adress Windowing Extension, AWE)机制利用超出限制的内存。在64位的系统上面Windows的内存寻址通常就不是个问题了,因为此时的Windows可寻址的VAS空间达16TB。

作为同样是Windows应用的SQL Server,自然也是无法独立于VAS规则之外的了。当SQL Server启动之后,数据库引擎就只能看到自己的VAS空间,缓存池(buffer pool)也就放在这个空间里面。缓存池缓存的是SQL Server数据:它由8KB大小缓存组成,缓存了来自数据文件的数据页,这个类似于Oracle的数据块缓存区。在VAS中的数据库引擎由 SQLSERVER.EXE程序,各个DLL库以及线程结构组成。

缓存池的大小受限于两个组件:机器可用的物理内存和SQL能访问的VAS。为避免缓存池无限扩展最终会吃掉所有的VAS而导致SQL Server意外死掉,SQL Server会让缓存池留下一部分VAS内存作为“保留区”。另外SQLOS内存管理器还保证缓存池可以满足其他SQL Server内部组件需要:缓存池中的内存页可以用来缓存连接数据、SQL优化器数据、还要做最重要的执行缓存之用。因为分配给这些组件的内存是不能再用作存储数据了,因此这些页又叫做被盗用页(stolen pages)。

下图简要的展示了SQL Server VAS的各个组件。

前面提到的资源监视器(resource monitor)属于SQLOS的一个组件。资源监视器的工作之一就是检查和监听操作系统发出的低内存通知,当资源监视器检测到低内存情况时,它会将这一情况记录到一个称为环缓冲区(Ring Buffer)的结构中,然后再将这个信息广播给SQL Server引擎,这样所有SQL相关的组件接到广播之后能减少它们的内存使用。SQLOS的内存管理器(Memory Manager)组件也同样会监视可用的虚拟内存和物理内存以及通过一个叫内存专员(memory clerk)的通知机制对内存压力做出响应。

在涉及到任务执行(查询编译、执行等等)的时候,SQLOS使用到一个叫做调度器的机制。调度器(scheduler)可以看成是SQL Server对于每个单个CPU的抽象表示,举个例子来说,一个运行SQL Server的机器有2颗4核的CPU的话那就能看到8个调度器。每个调度器都会和一定数量的工作线程(worker thread)相关联。例如在数据库上面执行的一个查询任务,这个任务会被分割成解析、编译、生成执行计划等一序列的子任务,每个这样的子任务都会由不同的线程来执行。

当SQL Server启动的时候,只会启动有限数量的线程。线程的总数取决于运行SQL Server的机器的CPU数量以及架构(x86还是x64)。一个少于4个CPU的x86系统,所创建的线程数量为256;超过4个CPU之后每个增加的CPU会多创建8个线程。对于少于4个CPU的x64系统,初始线程数量为512;超过4个CPU之后每个增加的CPU会多创建16个线程。也就是说机器的CPU越强大,SQL Server创建的线程就会越多。

当用户连接到SQL Server系统执行任务时,每个任务都会被分配给一个调度器。在SQL Server 2005之前,任务是按照轮流的方式分配的。自SQL Server 2005开始,任务分配则由SQLOS执行:任务会分配给最闲的调度器。

和Oracle一样,在SQL Server也是可以查看那些运行中的后台进程的。只要在数据库实例上执行sp_who2命令就可以看到那些后台运行的进程了。

当中的CHECKPOINT进程看名字就知道什么意思了。LOG WRITER进程等价于Oracle的LGWR进程:它负责将数据库的变化写到事务日志中。LAZY WRITER进程等价于 Oracle的DBWn进程:它的任务是将数据页的更改从缓存池中写入到数据文件中。有一点和Oracle不同的时候,在这里只有一个lazy writer进程负责写数据文件。其它还有一些后台进程是和SQLOS关联的:scheduler monitor,deadlock monitor和resource monitor等等。

top事务一致性(Transactional Consistency)和基于时间点的恢复(Point-in-time Recovery)

Microsoft SQL Server和Oracle一样内置都有针对于事务的保护机制。事务一致性的基本思想就是对于数据的更改不会马上反映到磁盘的文件当中,实际上这两者更新的都是被称为数据缓冲区(buffer cache)的内存区,同时还在一个称为日志缓存(log buffer)的内存中连续的记录对于数据所做的更改,这些内存区域的内容被不断的写入到磁盘文件当中。用于将数据缓存区和日志缓存区中的内容写入到磁盘文件当中的进程是两个不同的进程,不过将日志缓存的写入磁盘要比将数据缓冲写入磁盘要频繁的多了。

现在当用户成功的提交一个事务时,他所做的更改并不会马上写入到数据文件中。不过这些变更会被记录到日志缓冲中,同时在发送提交成功的信息给用户之前这些相应的日志缓存数据会被写入到磁盘文件当中。

正如之前所述,SQL Server将日志文件称为事务日志(Transaction Log),Oracle 则称为重做日志(Redo Log)。在SQL Server术语中,保存数据更改记录的内存块称为日志缓存(Log Buffer),Oracle则称为重做缓存(Redo Buffer)。抛开命名的差异,实际日志文件的作用却是一样的:在服务器意外宕机之后,数据库服务器会在服务重新启动之后检查文件的内容,如果在日志文件中发现已提交的事务而在数据文件中不存在时,这些已提交的事务就会被重新应用到数据文件中;如果日志文件中的记录显示事务没有完成或者是回滚了,那反映到数据文件中的变化也同样会回滚。在这个过程中的第一个部分称为重做(REDO),第二个部分称为撤销(译注:这里英文为 undo,也许说回滚合适些)。

SQL Server为每个数据库单独的维护了一份事务日志,一份数据库事务日志可以存在有多个事务日志文件。事务日志文件在数据库创建的同时创建,之后也可以继续追加。对Oracle来说,数据库指的是素有的物理文件和逻辑的表空间。Oracle的重做日志会记录对于所有表空间的更改。一个Oracle数据库至少需要2个日志文件才能正常操作,当然日志文件可以多余2个,但是决不能少于2个。

SQL Server事务日志和Oracle重做日志之间的一个重大的区别就在于事务日志没有逻辑分组一说,而重做日志则可以被分组在2个或更多个日志分组当中。每个Oracle数据库至少需要有2个日志分组,每个分组里面必须要有一个或多个日志文件,日志组中的日志文件被称为日志组成员 (member)。

Oracle每次都是将日志缓存中的重做日志一次写入到一个重做日志组中的所有文件中。在一个日志组中使用多个日志文件的容错技术称为多路技术(multiplexing)。在一个日志组写满之后,Oracle就会转到下一个日志组,这样的操作称为日志切换(log switching),在这个日志组满了之后又再转到下一个日志组,以此类推。当所有的日志都写满之后(不管是存在2个还是多个日志组),Oracle就会将第一个组中的日志清理掉,然后开启一轮新的写入循环。

SQL Server数据库的事务日志一样是顺序写入的,只是如果数据库不是运行在简单恢复模式或者是事务日志做了备份的话,日志是不会被自动清除掉的。如果一个数据库的逻辑事务日志空间满了之后,它对应的数据文件还无法增长的话,数据将无法继续处理用户操作。不过如果事务日志做过备份的话,那么SQL Server就会让新事务重用那些已备份日志所占用的空间,从这点上来说,SQL Server的逻辑事务日志是以一种“回环 (wrap-around)”的方式使用的。另外要强调的一点是SQL Server的事务日志文件的大小可以配置成自动扩展,而Oracle的重做日志文件则是有一个预定义大小的,如果不是手工更改这个大小的话,日志文件是不会自动增长的。

就数据可用性而言,两种平台都提供了基于时间点恢复(point-in-time recovery)的选项,当然必要的话也可以禁止掉(这个在测试或开发系统中是可行的)。要是SQL Server数据库的基于时间点恢复的功能的话就必须是在完全恢复模式(full recovery mode)下。不管出于何种恢复模式,每次数据的修改都会被数据库记录在它对应的事务日志里面,这些日志会一直保留到事务日志备份备份的时候。数据库的恢复模式还能调整成简单恢复模式(simple recovery mode),这种情况下的产生的事务日志会在每次检查点(checkpoint)发生时截断。检查点操作会将所有数据缓冲中已修改的数据写入到数据文件中,日志缓存中的日志信息写入到日志文件中。如果数据库运行在简单恢复模式下,检查点完成之后所有的检查点之前完成的事务所产生的事务日志都会被删除掉,因此SQL Server已经确定这些旧的已提交的事务已经确定被写入到数据文件中去了。

现在假设数据库检测到一个错误(物理的或逻辑的),这时都可以使用最后一个全备先还原数据库然后使用备份的事务日志恢复到还原的数据库上面,这种特性可以让数据库回到之前的任意一个时间点(译注:当然是指完全备份之后的任意一个时间点),只是这个特性只有在数据库使用完全恢复模式的时候才可用。

Oracle中也有类似的概念。Oracle可以运行在归档模式(ARCHIVELOG)或是非归档模式(NOARCHIVELOG)下。在非归档模式下,当日志组链上的最后一个组写满之后就会覆盖第一个日志组,这时因为 Oracle的重做日志组是按照循环使用的方式运作的。当然这也就意味着数据库是不能回到那些被删除了的事务记录所对应的时间点的。所以从功能上说,这就相当于SQL Server的简单恢复模式。

当Oracle数据库运行在归档模式下时,它同时也会在后台启动一个或者多个归档进程(archiver),归档进程的作用就是在一个日志组满了之后将日志组的内容备份到放在磁盘上的文件中,这些保存的日志文件被称为归档日志(archived log),日志组在归档之后就可以放心的被覆盖了。自己想想就会发现Oracle重做日志组归档的功能其实和SQL Server事务日志备份是一样的,也是说Oracle的归档模式和SQL Server的完全恢复模式是一样的。

Oracle和SQL Server一样,在还原全备之后可以使用归档日志进行还原。不同的时候SQL Server的事务日志备份需要手工的配置而Oracle只要配置了归档模式之后归档进行就会自动的进行日志归档备份操作。

最后,从恢复时间(recovery time)上面也能看到两个平台的相似之处。在之前也提到过,在数据引擎启动时会经过一个重做和撤销的阶段,这个阶段所花费的时候称为恢复间隔 (recovery interval)。很明显的数据库管理员是希望这个时间越短越好的,在SQL Server中DBA可以通过下面的命令来配置这个时间间隔:
1 sp_configure 'show advanced option', 1
2 reconfigure
3  
4 sp_configure 'recovery interval', <time-in-minutes>
5 reconfigure

这个命令修改的是系统配置参数。设置好恢复间隔之后,SQL Server会自动调整检查点进程执行检查点的时间来满足这个设置的时间要求。设置这个恢复间隔的单位是分钟。

Oracle中类似的设置就是平均恢复时间(Mean Time To Recovery, MTTR),可以通过修改初始化参数FAST_START_MTTR_TARGET来修改它,设置这个参数可以用来调整检查点执行的频率。这个值决定了Oracle再执行数据库崩溃恢复时所花费的时间,设置命令如下:
1 ALTER SYSTEM SET FAST_START_MTTR_TARGET=<number_of_seconds> SCOPE=spfile;