
Oracle工作过程及原理.doc
5页我们从一个用户请求开始讲,ORACLE 的完整的工作机制是怎样的,首先一个用户进程发出一个连接请求,如果使用的是主机命名或者是本地服务命中的主机名使用的是机器名(非 IP 地址),那么这个请求都会通过 DNS 服务器或 HOST 文件的服务名解析然后传送到 ORACLE 监听进程,监听进程接收到用户请求后会采取两种方式来处理这个用户请求, 下面我们分专用服务器和共享服务器分别采用这两种方式时的情况来讲:专用服务器模式下:一种方式是监听进程接收到用户进程请求后,产生一个新的专用服务器进程,并且将对用户进程的所有控制信息传给此服务器进程,也就是说新建的服务器进程继承了监听进程的信息,然后服务器进程给用户进程发一个 RESEND 包,通知用户进程可以开始给它发信息了,用户进程给这个新建的服务器进程发一个 CONNECT 包,服务器进程再以 ACCEPT 包回应用户进程,致此,用户进程正式与服务器进程确定连接我们把这种连接叫做 HAND-OFF 连接,也叫转换连接另一种方式是监听进程接收到用户进程的请求后产生一个新的专用服务器进程,这个服务器进程选用一个 TCP/IP 端口来控制与用户进程的交互,然后将此信息回传给监听进程,监听进程再将此信息传给用户进程,用户进程使用这个端口给服务器进程发送一个 CONNECT 包,服务器进程再给用户进程发送一个 ACCEPT 包,致此,用户进程可以正式向服务器进程发送信息了。
这种方式我们叫做重定向连接HAND-OFF 连接需要系统平台具有进程继承的能力,为了使 WINDOWS NT/2000 支持 HAND-OFF 必须在HKEY_LOCAL_MACHINE>SOFTWARE>ORACLE>HOMEX 中设置 USE_SHARED_SOCKET共享服务器模式下:只有重定向连接的方式,工作方式是监听进程接收到用户进程的请求后产生一个新的调度进程,这个调度进程选用一个 TCP/IP 端口来控制与用户进程的交互,然后将此信息回传给监听进程,监听进程再将此信息传给用户进程,用户进程使用这个端口给调度进程发送一个 CONNECT 包,调度进程再给用户进程发送一个 ACCEPT 包,致此,用户进程可以正式向调度进程发送信息了可以通过设置MAX_DISPIATCHERS 这个参数来确定调度进程的最大数目,如果调度进程的个数已经达到了最大,或者已有的调度进程不是满负荷,监听进程将不再创建新的调度进程,而是让其中一个调度进程选用一个TCP/IP 端口来与此用户进程交互调度进程每接收一个用户进程请求都会在监听进程处作一个登记,以便监听进程能够均衡每个调度进程的负荷,所有的用户进程请求将分别在有限的调度进程中排队,所有调度进程再顺序的把各自队列中的部分用户进程请求放入同一个请求队列,等候多个 ORACLE 的共享服务器进程进行处理(可以通过 SHARED_SERVERS 参数设置共享服务器进程的个数),也就是说所有的调度进程共享同一个请求队列,共享服务器模式下一个实例只有一个请求队列,共享服务器进程处理完用户进程的请求后将根据用户进程请求取自不同的调度进程将返回结果放入不同的响应队列,也就是说有多少调度进程就有多少响应队列,然后各个调度进程从各自的响应队列中将结果取出再返回给用户进程。
以上我们讲完了用户与 ORACLE 的连接方式,下面我们要讲 ORACLE 服务器进程如可处理用户进程的请求,当一个用户进程发出了一条 SQL 语名:UPDATE TABBLEA SET SALARY=SALARY*2;首先,服务器进程把这条语句的字符转换成 ASCII 等效数字码,接着这个 ASCII 码被传递给一个 HASH 函数,并返回一个 HASH 值,服务器进程将到 SHARED POOL 的共享 PL/SQL 区去查找是否存在同样的 HASH 值,如果存在,服务器进程将使用这条语句已高速缓存在 SHARED POOL 中的已分析过的版本来执行,如果不存在,服务器进程将对该语句进行语法分析,首先检查该语句的语法的正确性,接着对语句中涉及的表、索引、视图等对象进行解析,并对照数据字典检查这些对象的名称以及相关结构,并根据 ORACLE 选用的优化模式以及数据字典中是否存在相应对象的统计数据和是否使用了存储大纲来生成一个执行计划或从存储大纲中选用一个执行计划,然后再用数据字典核对此用户对相应对象的执行权限,最后生成一个编译代码ORACLE 将这条语名的本身实际文本、 HASH 值、编译代码、与此语名相关联的任何统计数据和该语句的执行计划缓存在 SHARED POOL 的共享 PL/SQL 区。
服务器进程通过 SHARED POOL 锁存器来申请可以向哪些共享 PL/SQL 区中缓存这此内容,也就是说被 SHARED POOL 锁存器锁定的 PL/SQL 区中的块不可被覆盖,因为这些块可能被其它进程所使用在 SQL 分析阶段将用到 LIBRARY CACHE,从数据字典中核对表、视图等结构的时候,需要将数据字典从磁盘读入 LIBRARY CACHE,因此,在读入之前也要使用 LIBRARY CACHE 锁存器来申请用于缓存数据字典生成编译代码之后,接着下一步服务器进程要准备开始更新数据,服务器进程将到 DB BUFFER 中查找是否有相关对象的缓存数据,下面分两个可能进行解释:如果没有,服务器进程将在表头部请求一些行锁,如果成功加锁,服务器进程将从数据文件中读这些行所在的数据块放入 DB BUFFER 中空闲的区域或者覆盖已被挤出 LRU 列表的非脏数据块缓冲区,并且排列在LRU 列表的头部,如果这些非脏数据缓冲区写完也不能满足新数据的请求时,会立即触发 DBWN 进程将脏数据列表中指向的缓冲块写入数据文件,并且清洗掉这些缓冲区,来腾出空间缓冲新读入的数据,也就是在放入 DB BUFFER 之前也是要先申请 DB BUFFER 中的锁存器,成功锁定后,再写入 DB BUFFER,然后服务器程将该语句影响的被读入 DB BUFFER 块中的这些行的 ROWID 及将要更新的原值和新值及 SCN等信息逐条的写入 REDO LOG BUFFER,在写入 REDO LOG BUFFER 之前也是先请求 REDO LOG BUFFER 块的锁存器,成功锁定之后才开始写入,当写入达到 REDO LOG BUFFER 大小的三分之一或写入量达到 1M 或超过三秒后或发生检查点时或者 DBWN 之前发生, LGWR 将把 REDO LOG BUFFER 中的数据写入磁盘上的重做日志文件,已被写入重做日志文件的 REDO LOG BUFFER 中的块上的锁存器被释放,并可被后来写入的信息所覆盖,REDO LOG BUFFER 以循环的方式工作。
当一个重做日志文件写满后,LGWR 将切换到下一个重做日志文件,如果是归档模式,归档进程还将前一个写满的重做日志进程写入归档日志文件,重做日志文件也是循环工作方式写完所有的 REDO LOG BUFFER 之后,服务器进程开始改写这个 DB BUFFER 块头部的事务列表并写入 SCN,然后 COPY 包含这个块的头部事务列表及 SCN 信息的数据副本放入回滚段中,我们将回滚段中的副本称为数据块的“前映像”回滚段可以存储在专门的回滚表空间中,这个表空间由一个或多个物理文件组成,并专用于回滚表空间,回滚段也可在其它表空间中的数据文件中开辟然后改写这个 DB BUFFER 块的数据,并在其头部写入对应的回滚段地址,如果对一行数据多次 UPDATE 而不 COMMIT 则在回滚段中将会有多个“前映像”,除第一个“前映像”含有 SCN信息外,其它的每个“前映像”的头部还含有 SCN 信息和“前前映像 ”的回滚段地址一次 UPDATE 操作只对应一个 SCN然后服务器进程在脏数据列表中建立一条指向此缓冲块的指针接着服务器进程会从数据文件读入第二个块重复以上读入,记日志,建立回滚段,修改,放入脏列表的动作,当脏数据列表达到一定长度时,DBWN 进程将脏数据列表中指向的缓冲块全部写入数据文件,也就是释放加在这些 DB BUFER 块上的锁存器。
其实 ORACLE 可以一次从数据文件中读入几个块放入 DB BUFFER,可以通过参数 DB_FILE_MULTIBLOCK_READ_COUNT 来设置一次读入的块的个数如果要查找的数据已缓存,则根据用户的 SQL 操作类型决定如何操作,如果是 SELECT 则查看 DB BUFFER 块的头部是否有事务,如果有,将从回滚段读取,如果没有则比较 SELECT 的 SCN 与 DB BUFFER 块头部的 SCN 如果比自己大,仍然从回滚段读取,如果比自己小则认这是一个非脏缓存,可以直接从这个 DB BUFFER 块中读取如果是 UPDATE 则即使在 DB BUFFER 中找到一个没有事务,而且SCN 比自己小的非脏缓存数据块,服务器进程仍然要到表的头部对这条记录申请加锁,加锁成功则进行后续动作,如果不成功,则要等待前面的进程解锁后才能进行动作只有当 SQL 语句影响的所有行所在的最后一个块被读入 DB BUFFER 并且重做信息被写入 REDO LOG BUFFER(仅是指重做日志缓冲,而非重做日志文件)之后,用户才可以发出 COMMIT,COMMIT 触发LGRW,但并不强制立即 DBWN 来释放所有相应的 DB BUFFER 块上的锁,也就是说有可能出现已COMMIT,但在随后的一段时间内 DBWN 还在写这条语句涉及的数据块的情形,表头部的行锁,并不是在 COMMIT 一发出就马上释放,实际上要等到相应的 DBWN 进程结束才会释放。
一个用户请求锁定另一个用户已 COMMIT 的资源不成功的机会是存在的,从 COMMIT 到 DBWN 进程结束之间的时间很短,如果恰巧在这个时间断电,由于 COMMIT 已触发 LGWR 进程,所以这些未来得及写入数据文件的改变会在实例重启后由 SMON 进程根据重做日志文件来前滚如果未 COMMIT 就断电,由于 DBWN 之前触发LGWR,所有 DBWN 在数据文件上的修改都会被先一步记入重做日志文件,实例重启后, SMON 进程再根据重做日志文件来回滚如果用户 ROOLBACK,则服务器进程会根据数据文件块和 DB BUFFER 中块的头部的事务列表和 SCN 以及回滚段地址找到回滚段中相应的修改前的副本,并且用这些原值来还原当前数据文件中已修改但未提交的改变如果有多个“前映像”,服务器进程会在一个“前映像”的头部找到“前前映像”的回滚段地址,一直找到同一事务下的最早的一个“前映像”为止一旦发出了 COMMIT,用户就不能 ROOLBACK,这使得COMMIT 后 DBWN 进程还没有全部完成的后续动作得到了保障下面我们要提到检查点的作用,当一个全部检查点发生的时候,首先让 LGWR 进程将 REDO LOG BUFFER 中的所有缓冲(包含未提交的重做信息)写入重做日志文件,然后让 DBWN 进程将 DB BUFFER中所有已提交的缓冲写入数据文件(不强制写未提交的)。
然后更新控制文件和数据文件头部的 SCN,表明当前数据库是一致的,如果在发生检点之前断电,并且当时有一个未提交的改变正在进行,实例重启之后,SMON 进程将从上一个检查点开始核对这个检查点之后记录在重做日志文件中已提交的和未提交改变,因为 DBWN 之前会触发 LGWR,所以 DBWN 对数据文件的修改一定会被先记录在重做日志文件中因此,断电前被 DBWN 写进数据文件的改变将通过重做日志文件中的记录进行还原,叫做回滚,如果断电时有一个已提交,但 DBWN 动作还没有完全完成的改变存在,因为已经提交,提交会触发 LGWR 进程,所以不管 DBWN 动作是否已完成,该语句将要影响的行及其产生的结果一定已经记录在重做日志文件中了,则实例重启后,SMON 进程根据重做日志文件进行前滚。
