首页 > 数据库 >Oracle编程艺术

Oracle编程艺术

时间:2022-10-06 15:22:40浏览次数:68  
标签:文件 艺术 数据库 编程 进程 内存 Oracle 服务器

第二章 - 体系结构

1.绑定变量,不写死常量: 1)绑定变量的话,因为SQL语句是不变的,数据库只解析一次语句(软解析),但是如果绑定的是常量的话,则会多次解析(硬解析),效率就会变慢,差一个数量级。 硬解析一个查询时,数据库会更长时间的占用一种低级串行化设备,称为闩。 for i in 1 .. 100000 loop execute immediate 'insert into t values (:x)' using i; -- 绑定变量,时间:3.932 execute immediate 'insert into t values (' || i || ')'; -- 绑定常量,时间:29.701 end loop; 2)使用拼接字符会有“SQL注入”的危险。 2.并发控制: 1)锁机制: oracle只在修改时对数据加“行级锁”,正常情况下不会升级到“块级锁”或“表级锁”。 如果只是读数据,oracle绝不会对数据加锁。 写操作不会阻塞读操作(读不会被写阻塞)。 如果会话A想改一行数据,但是这行数据已经被另外一个会话B锁定,此时会话A才会被阻塞。但是如果会话A仅仅是想读数据则不会被阻塞。 2)多版本控制: 读一致查询:对于一个给定的时间点,查询会产生一致的结果。 非阻塞查询:查询的会话不会被写入的会话阻塞,但在其他数据库中可能不是这样。 3)实际上是DELETE命令在删除数据之前,把这些数据放在一个被称为undo段(undo segment)的数据区,这个数据区也被称为回滚段(rollback segment) DECLARE CURSOR C_EMP IS SELECT * FROM t; c_row t%rowtype; BEGIN OPEN C_EMP; DELETE FROM t; COMMIT; LOOP fetch C_EMP into c_row; EXIT WHEN C_EMP%NOTFOUND; DBMS_OUTPUT.put_line(c_row.username); -- 虽然前面已经删除了,但是结果是有输出的 END LOOP; CLOSE C_EMP; END; 4)闪回: 从9i版本开始,我们可以通过闪回查询特性来指示oracle提供任何指定时间的查询结果(对于这个时间可能会有一些限制)。 使用闪回查询(即as of scn 或者 as of timestamp)来看某个时间点上表有什么 select cnt_now, cnt_then, :scn then_scn, dbms_flashback.get_system_change_number now_scn from (select count(*) cnt_now from emp), (select count(*) cnt_then from emp as of scn :scn) 5)读一致性和非阻塞读 oracle会利用多版本控制来得到结果,也就是查询开始是那个时间点的结果,在查询的过程中,不需要对任何数据加锁。 只要我们更新数据,oracle就会在2个不同的位置(redo、undo)进行记录(大多数其他数据库可能会把redo/undo放在一起,并将其当成“事务数据”)。放在redo中的记录用于重做或者“前滚”事务,比如insert oracle就会在redo记下插入的行;如果是delete,则是一个简单的消息(要删除的行放在undo)。放在undo中的记录是为了对应事务失败或者回滚而准备的,此时,oracle会undo中读取“修改前的数据镜像”来 恢复数据(当时的scn号来确定当时读取的数据)。此外oracle还会用它构建数据库修改前的状态,也就是说可以把块恢复到查询开始时的状态。这样一来,你不仅可以得到正确一致的答案,也不会对任何数据加锁。 体系结构概述: 数据库:操作系统文件或磁盘的集合。当使用oracle的自动存储管理或裸设备分区时,数据库可能不是操作系统中单独的文件,但是定义任然不变。 1.单租户数据库:这种数据库完整的包含了全套的数据文件、控制文件、重做日志文件、参数文件等等。它包含了oracle内部使用的所有的元数据(比如ALL_OBJECTS的定义)、数据、代码(比如DBMS_OUTPUT), 以及所有应用的元数据、数据和代码。版本12c之前所有的oracle数据库都是这种类型的数据库。 2.容器数据库或根数据库(CDB):这种数据库包含了一整套数据文件、控制文件、重做日志文件及参数文件等,但它仅用于存放oracle的元数据、数据库自用数据库及oracle内部代码。在这个数据库的数据文件中不(应) 存放应用的数据及代码而仅仅(应)存放上面提到的oracle自用的实体。这种数据库是完全自包含的,它不依赖于其他对象就可以被实例装载和打开。 3.可插拔式数据库(PDB):这种数据库仅包含数据文件。它并不是完全自包含的:我们必须将其插在某个容器数据库上才可对其打开进而进行读写。这个数据库仅包含应用的元数据及应用的对象、数据及代码,它不包含oracle 的元数据或oracle的内部代码。它仅仅包含数据文件,没有(以前我们所常见的oracle数据库的)重做日志文件、控制文件、参数文件等,但它在使用过程中会利用其所插在的CDB上的这些类型的文件。 实例:一组oracle后台进程/线程以及一个共享内存区域,这些内存由同一台电脑上运行的线程/进程所共享。oracle在这里存放及维护易失的、非持久性的内容(有些可以刷到磁盘)。需要注意的是,就算没有磁盘存储, 数据库实例也能存在。 数据库和实例的关系:单租户或容器数据库可以由多个实例装载和打开,而实例在任何时间点只能装载和打开一个数据库。实际上,准确地讲,实例在其整个生存期中最多能装载和打开一个数据库! 对于可插拔式数据库来说,它在任何时间点只能与一个容器数据库关联,进而(间接地)只能与一个实例关联,实例打开并装载了CDB之后,其包含的PDB也会使用这个实例。 因此,与CDB一样,PDB在任何时刻可能会被一个或多个实例打开。但与CDB/单租户数据库不同的是,一个实例每次可以同时访问许多(最多约250)个PDB,即每个实例可以为 多个PDB同时提供服务,但是它只能打开一个CDB或单租户数据库。 重申一遍: 1.实例是一组后台进程和共享内存 2.数据库(CDB或单租户数据库)是磁盘上存储的数据集合。 3.实例“一生”只能装载并打开一个数据库。后面我们会看到“可插拔式数据库”可多次的“打开”并“关闭”,但是对于像单租户数据库及容器数据库这样的完全自包含的数据库来说,每个实例只能打开/关闭一次。 4.数据库可以由一个或多个实例(使用RAC)装载和打开,装载一个数据库的实例数量可能会随时变化。 SGA和后台进程: oracle有一个很大的内存块,称为系统全局区(SGA)用于(但不限于) 1.维护所有进程需要访问的内部数据结构 2.缓存磁盘上的数据,另外重做数据写至磁盘之前先在这里缓存 3.保存已解析的SQL计划,等等 oracle有一组“附加到”SGA的进程,附加机制因操作系统而异。在Unix/Linux环境中,这些进程会附到一个很大的共享内存段,这是操作系统中分配的一个内存块,可以由多个进程并发的访问。 在windows中,这些进程使用C调用(malloc())来分配内存,因为它们实际上是一个大进程中的线程,所以会共享相同的虚拟内存空间。 连接oracle:oracle服务器处理请求的2种最常见的方式:专用服务器和共享服务器 专用服务器: 在我们登录时,oracle总会为我们创建一个新的进程。这通常称为专用服务器配置,因为这个服务器进程会在我们的会话生存期中专门为我们服务。对于每一个会话,都会出现一个新的专用服务器进程, 会话与专用服务器之间存在一对一的映射。这个服务器不是实例的一部分,我们的客户进程(或任何连接数据库的程序)会通过某种网络通道(如TCP/IP套接字)与这个专用服务器直接通信,并由这个服务器 进程接收和执行我们的SQL。 共享服务器: 数据库不会对每个用户连接创建新的线程或Unix/Linux进程。在共享服务器中,oracle使用一个“共享进程”池为多个用户提供服务。共享服务器实际上就是一种连接池机制。利用共享服务器,我们不必为10000个 数据库会话创建10000个专用服务器进程/线程(这样进程/线程就太多了),而只需建立很少的一部分进程/线程。这些进程/线程由所以会话共享,这个oracle就能让更多的用户与数据库连接。共享服务器通常与 实例一同启动。 共享服务器连接和专用服务器连接之间有一个重大区别:与数据库连接的客户进程不会与共享服务器直接通信,但专用服务器则不然,客户进程会与专用服务器直接通信。之所以不能与共享服务器直接对话,原因是 在于这个服务器进程是共享的。为了共享这些进程,还需要另外一个机制,通过这种机制才能与服务器进程对话。为此,oracle使用一个或一组称为调度程序的进程。客户进程通过网络与一个调度程序进程通信,这个 调度程序进程将客户的请求放到SGA中的请求队列(这也是SGA的用途之一)。即需要调度程序把多个客户请求放到SGA的请求队列上再排队分配到多个共享服务器。 可插拔式数据库: PDB在多租户架构下是一组非自包含的数据文件集合,它仅包含应用的数据与元数据。oracle自用的数据并不存放在这些数据文件中,而放在容器数据库中。如果要使用或者查询PDB,我们就必须要将其插到一个容器数据库中。 CDB中只有oracle自用的数据及元数据,这些是oracle运行时所需的必要的信息。PDB会存放“剩下的”数据及元数据。 oracle设计PDB及多租户架构主要有2个目的: 1.在单个主机上,有效的降低多个数据库/应用对资源的使用量。 2.在单个主机上,降低DBA对多个数据库/应用进行维护的工作量。

 

 

第三章 - 文件

与实例相关的8类主要文件: 1.参数文件:这些文件告诉oracle实例在哪里可以找到控制文件,应当给某种内存结构设置多大空间等。 2.跟踪文件:这通常是一个服务器进程对某种异常错误条件作出响应时创建的诊断文件。 3.警告文件:与跟踪文件类似,但是包含“期望”事件的有关信息,并且通过一个集中式文件(其中包括多个数据库事件)警告DBA。 4.数据文件:这些文件是数据库中主要文件,其中包括表、索引和所有其他的段。 5.临时文件:这些文件能告诉你数据文件、临时文件和重做日志文件在哪里,还会指出与文件状态有关的其他元数据。RMAN(oracle提供的备份恢复工具)也会在这个文件存放数据库备份的信息 6.重做日志文件:这些就是事务日志。 7.密码文件:这些文件用于认证数据库的SYS角色用户。 从oracle database 10g开始,又增加2种新的可选文件,它们可以帮助oracle实现更快的备份和恢复操作。 1.修改跟踪文件:这个文件用于oracle数据库建立增量备份。修改跟踪文件不一定非得放在快速恢复区。 2.闪回日志文件:这些文件存储数据库块的“前映象”,它用于新增的FLASHBACK DATABASE命令。 通常与数据库有关的其他类型的文件: 1.转储文件:这些文件由oracle提供的一个工具exp(导出)生成,并由工具imp(导入)使用。需要注意的是exp命令在当前版本已经被弃用了,但是imp命令还是可以继续使用,这是 为了支持数据库从老版本数据库导入到新版本的数据库当中。 2.数据泵文件:这些文件由oracle数据泵expdp导出进程生成,并由数据泵impdp导入进程使用。外部表也可以创建和使用这种文件格式。 3.平面文件:这些无格式文件可以在文本编辑器中查看。通常会使用这些文件向数据库中加载数据。 以上文件中最重要的是数据文件和重做日志文件。 参数文件: 数据库的参数文件通常称为初始化文件,或init.ora文件。这是因为历史上它的默认名就是init<ORACLE_SID>.ora。之所以称之为“历史上”的默认名,原因是从Oracle Datebase 9iRelease1以来,对于存储数据库的参数 设置,引入了一个有很大改进的新方法:服务器参数文件,简称为SPFILE。这个文件的默认名为spfile<ORACLE_SID>.ora 什么是参数: 我们可以把数据库参数看成是一个键值对。 init.ora 参数文件(PFILE参数文件): Oracle init.ora 文件相当简单,里面包含一些键值对。文件示例: control_files='/u01/dbfile/ORA12CR1/control01.ctl','/u01/dbfile/ORA12CR1/control02.ctl' db_block_size=8192 db_name='ORA12CR1' 参数文件由很多参数可以使用默认值而不用设置,但是它至少要告诉示例2件事:1.数据库的名字 2.控制文件的位置。Oracle是从控制文件来找到其他文件的位置。 默认情况下: 名字: init$ORACLE_SID.ora (UNIX/LINUX 环境变量) init%ORACLE_SID%.ora (Windows 环境变量) 目录: $ORACLE_HOME/dbs (UNIX/LINUX) %ORACLE_HOME%\DATABASE (Windows) 缺点: init.ora参数文件不一定位于数据库服务器上,之所以会引入存储参数文件,原因之一就是为了补救这种情况。当你在客户端使用init.ora参数文件来启动数据库时,客户端必须有这个文件。 因此Oracle引入了服务器参数文件,数据库可以将其作为参数设置的、唯一正确的“信息来源”。 服务器参数文件: 在访问和维护实例参数设置方面,SPFILE是Oracle做出的一个重要改变。有了SPFILE,可以消除传统参数文件存在的2个严重问题; 1.它杜绝了参数文件的繁殖。SPFILE总是存储在数据库服务器上,它必须存在服务器主机本身,不能放在客户机上,这样就只有一个“正确的”参数“来源”。 2.它再也无需(实际是不能)在数据库之外使用文本编辑器手动的维护参数文件。你必须利用ALTER SYSTEN 命令才能将参数设置写入SPFILE。 命名规则: $ORACLE_HOMR/dbs/spfile$ORACLE_SID.ora (UNIX/LINUX 环境变量) %ORACLE_HOMR/database/spfile%ORACLE_SID%.ora (Windows 环境变量) 1.从PFILE转换为SPFILE: 命令: create SPFILE from pfile; 从SPFILE参数文件可以看出:集群中所有实例共享的参数设置都以*.开头。单个实例特有的参数设置都以实例名(Oracle SID)为前缀。 2.设置SPFILE中的参数值: SPFILE是二进制文件,它们不可以用文本编辑器来编辑。需要使用alter system 命令,语句如下(<>中的部分是可选的,其中的管道符号(|)表示“取候选列表中的一个选项”) 默认情况下,alter system set 命令会更新当前会话所连接的实例(如果使用可插拔式数据库,则会更新当前连接的可插拔式数据库的数据字典,并且同时更新SPFILE。 Alter system set parameter=value <comment='text'> <deferred> <scope=memory|spfile|both> <sid='sid|*'> <container=current|all> parameter=value:表示我们要改的参数名以及参数的新值。 comment='text':是一个与此参数设置相关的可选注释。 deferred:指定系统修改是否只对以后的会话生效(对当前建立的会话无效,包括执行此修改的会话),默认情况下是会立即生效,但是有些参数不能“立即”修改,只有新建的会话才能 使用这些修改之后的参数。这个命令可以查看哪些参数需要用到这个参数:SELECT NAME FROM V$parameter WHERE issys_modifiable = 'DEFERRED'; scope=memory|spfile|both:指示了这个参数设置的“作用域”。设置参数值时作用域有以下选择。 scope=memory:只在实例中修改,数据库重启后本次修改将会丢失。下次重启数据库时,此参数设置以SPFILE为准。 scope=SPFILE:只修改SPFILE只的值。数据库重启之后这个修改才会生效。有些参数只能使用这个选项来修改,例如:processes参数就必须使用SCOPE=SPFILE。 scope=both:指内存和SPFILE中都会完成参数修改 sid='sid|*':主要用于集群环境,默认值为sid='*',SID选项可为集群中的每个实例单独的指定参数。如果不使用Oracle RAC,一般不需要指定SID=。 container=current|all:适用于多租户架构的数据库,用于指定参数设置是应用到当前连接的或者所有可插拔式数据库。可插拔式数据库的参数存储于其自身的数据字典当中,而非SPFILE 当我们将其移动到另外一个容器时,其自身的参数设置也会随之一并转移。 3.取消SPFILE中的设置: 命令: Alter system reset parameter <scope=memory|spfile|both> sid='sid|*' 4.从SPFILE创建PFILE 命令: create PFILE from spfile; 这个命令根据二进制格式的SPFILE文件创建一个纯文本格式的PFILE文件,生成这个文件可以在任何文本编辑器中编辑,并且以后可以用来启动数据库。正常情况下,使用这个命令至少有2个原因 1.创建一个“一次性”参数文件,用于启动数据库来完成维护工作。执行命令生成PFILE参数文件,再对PFILE文件进行修改。重启数据库,注意不能使用SPFILE来启动,而是要用刚才编辑的PFILE 并且在STARTUP命令中使用从句PFILE=<FILENAME>。进行维护工作后,正常启动数据库。 2.维护修改历史,在注释中记录修改。SPFILE不支持历史注释记录。可以备份参数PFILE来实现。 5.修正被破坏的SPFILE: 可以从警告日志恢复参数文件的信息。每次启动数据库时,警告日志都会显示出SPFILE的信息。 有了内容就可以创建一个SPFILE,再用create SPFILE来转换为新的SPFILE 6.可插拔式数据库: 可插拔式数据库由一组文件构成,你可以方便的将其从一个容器数据库移动到另外一个上。在移动的过程中,PDB内所包含的所有的模式、用户、元数据、授权、数据以及PDB的参数设置(非从CDB 继承的参数)也会一并转移。PDB的参数之所以能够转移,因为Oracle将其存放在一个字典表sys.PDB_SPFILE$中。 跟踪文件: 跟踪文件能提供调试信息。服务器遇到问题时,它会生成一个包含大量诊断信息的跟踪文件。 Oracle数据库具有良好的可测量性,数据库中这种可测量性反映在以下几个方面: 1.V$视图:大多数V$视图都包含“同时”信息。V$WAITSTAT、V$SESSION_EVENT还有其他许多V$视图能让我们知道内核到底发生了什么。 2.审计命令:利用这个命令,能指定数据库要记录哪些事件以便日后分析。 3.资源管理器:这个特效允许对数据库所使用的资源进行更精细的管理。Oracle的资源管理器充分利用了内核中的可测量代码来了解数据库运行时 各种资源的使用状况,这样它就有可能对这些资源的使用做进一步的控制。 4.Oracle“事件”:能让Oracle生成所需的跟踪或诊断信息。 5.DBMS_TRACE:这是PL/SQL引擎中的一个工具,它会全面的记录存储过程的调用树、产生的异常以及遇到的错误。 6.数据库事件触发器:这些触发器允许你监控和记录你觉得“意外”或非正常的情况。例如。你可以记录发生“临时空间用尽”错误时正在运行的SQL。 7.SQL_TRACE/DBMS_MONITOR:这个工具用于查看数据库执行的SQL语句、等待事件以及其他特效/行为的诊断信息。我们还可以通过一些Oracle的扩展功能比如说 10046 Oracle Event来启动SQL跟踪。 跟踪文件可分成2大类: 1.计划内、由用户请求所产生的跟踪文件:如启用DBMS_MONITOR.SESSION_TRACE_ENABLE 产生的跟踪文件。这类文件包含会话相关的诊断信息,它有助于你调整及优化应用的性能, 并诊断出瓶颈到底在哪里。 2.计划外、数据库服务器自动产生的跟踪文件:当数据库服务器发生某种严重错误时,它会自动生成这些跟踪文件。这些错误包括(但不限于)ORA-0600 “Internal Error” (内部错误)。 这些跟踪文件包含一些诊断信息,它主要对Oracle Support 的分析人员有用,但对我们来说,除了能看出应用中哪里出现内部错误外,用处不大。 计划内、由用户请求所产生的跟踪文件: 这类跟踪文件通常可由DBMS_MONITOR 来生成。这些跟踪文件包含与诊断和性能有关的信息。它们对于了解数据库应用的内部工作有着非凡的意义。在一个正常运行的数据库中,这类跟踪文件最为常见。 1.跟踪文件的位置: 不论是使用SQL_TRACE/DBMS_MONITOR还是扩展的跟踪工具,Oracle都会在数据库服务器主机的以下2个位置之一生成跟踪文件: 1.如果使用专用服务器连接,会在 user_dump_dest 参数指定的目录中生成跟踪文件。 2.如果使用共享服务器连接,则在 backgroud_dump_dest 参数指定的目录中生成跟踪文件。 我们有很多途径可以指定跟踪文件的存放位置:比如你可以从SQL*PLUS执行 show parameter dump_dest,也可以直接查询V$PARAMETER视图。 background_dump_dest:后台转储目标用于所以“服务器”进程。 core_dump_dest:当发生严重问题时(如进程崩溃)服务器会在内核转储目标自动生成相应的跟踪文件,其中包含了发生异常的进程的详细信息。 user_dump_dest:用于专用/共享服务器的跟踪文件 在Oracle Database 11g 增加Default Trace File信息之前,你必须手动(通过组织各种信息)才能查找到跟踪文件。如果你使用的是一个共享服务器连接,那么真正为你服务的是一个后台进程, 所以跟踪文件的位置由 backgroud_dump_dest 确定。如果你在使用一个专用服务器连接,那你是使用一个用户或前台进程与Oracle交互,因此跟踪文件会放在由 user_dump_dest 参数指定的 目录中。一旦出现一个严重的Oracle内部错误,或者Oracle Support 要求你生成一个跟踪文件来得到额外的调试信息,这时生成“内核”转储文件,而 core_dump_dest 参数就定义这类“内核” 文件还在那里生成。 2.命名约定: 跟踪文件名可以分为以下几个部分: 1.文件名的第一部分是 ORACLE_SID (9i Release 1 例外) 2.文件名的下一部分只有一个 ora 3.跟踪文件名中的数字部分是专用/共享服务器在操作系统中的进程ID,可以在 V$PARAMETER 视图得到。 3.对跟踪文件加标记: 当我们无权限访问 V$PARAMETER 和 V$SESSION 的时候,想得到跟踪文件的名字就比较困难。这时就可以对跟踪文件“加标记”来找到它。 alter SESSION set tracefile_identifier = 'Look_For_Me'; 这个命令就会使跟踪文件名包含这个“Look_For_Me”,用ls就可以找到这个文件。 计划内、由用户请求所产生的跟踪文件: 这些跟踪文件不是给你我用的,它们只对Oracle Support 有用。不过,当我们向 Oracle Support 提交服务请求时,这些跟踪文件会很有用。这样做很重要:如果你碰到了数据库的内部错误,更正这个错误 的唯一办法就是提交一个服务请求。 警告文件: 警告文件就是数据库的日记。这是一个简单的文本文件,数据库从创建那天起就会不停的写入改文件,直到数据库被删除为止。在这个文件中你可以看到数据库的“编年史”。比如在线日志文件的切换、内部错误、 表空间的创离线以及恢复为在线等等。 数据文件: 数据文件和重做日志文件是数据库中最重要的文件,你的数据库最终会存储在这些文件中。每个数据库都至少有一个数据文件,通常不止一个。用最简单的crate database 命令根据默认设置创建一个数据库, 这个数据库会有3个数据文。其中一个对应system 表空间(存放Oracle的数字字典),一个对应sysaux表空间(在10g 及以上版本中,非字典对象都存储在这个表空间中),最后一个对应user表空间。 所有数据库都至少有这3个数据文件。 .../dbs1ora12c.dbf .../dbx1ora12c.dbf .../dbu1ora12c.dbf 简单回顾文件系统的机制: 在Oracle中,可以用4种文件系统机制存储你的数据(12c只有3种)。这里指你的数据是指数据字典、redo、undo记录、表、索引以及LOB等,也就是你每天关注的数据。有以下几种方式存放这些数据。 1.格式化好(cooked)的操作系统(OS)文件系统:数据库的文件就像文字处理文档一样放在文件系统中。在Windows上你可以使用资源管理器来查看这些文件,在UNIX/LINUX上,可以通过ls命令。 你也可以使用简单的OS工具(如Windows上的xcopy或UNIX/LINUX上的cp)来移动文件。Cooked OS 文件一直是 Oracle 中存储数据的“最流行”的方法,不过我个人认为,随之ASM的引入,这种情况 会有所改观。数据库服务器中,格式化好的文件系统通常也会缓存数据,也就是说在数据库读写磁盘时,OS会为你缓存一些信息。 2.裸分区:这不是文件,而是裸磁盘。你不能用ls来查看,也不能在Windows资源管理器中查看其内容。它们就是磁盘上的一些大扇区,上面没有任何文件系统。对Oracle来说,整个裸分区就是一个大文件, 这与前面的文件系统不同,文件系统上可能有几十个甚至数百个数据库文件。目前,只有极少数Oracle数据库使用裸分区,这是因为裸分区的管理开销很大。此外,裸分区没有缓冲,所以的 I/O都是对存储设备的直接操作,它没有任何OS缓冲(对于数据库来说,这通常是个优点)。 (Oracle从11g就已经弃用裸分区,并且在12c中不再提供支持) 3.自动存储管理(ASM):这是Oracle Database 10g Release 1 的一个新特性。在11g r2 之前,ASM是一个仅供数据库使用的文件系统,你可以简单的把它看做一个数据库文件系统。在这个文件系统上, 你不能存放如<购物清单.txt>这样的文件,只能存储与数据库相关的信息:表、索引、备份、控制文件、参数文件、重做日志文件以及归档文件等。不过,即使是ASM,也同样、 存在着数据文件。它概念上讲,数据库仍存储在文件中,不过现在的文件系统是ASM。ASM可以在单机或者集群环境中工作。从11g R2起,你甚至还可以通过ASM构建一个集群文件系统 4.集群文件系统(ACFS):Oracle RAC (真正应用集群)需要使用这种文件系统,它为集群中所有的节点提供一个共享的文件系统。传统的文件系统只能由集群环境中的一台计算机使用。你可以使用诸如Samba或者NFS 服务(Samba与NFS类似,可以在Windows/UNIX/LINUX环境之间共享磁盘),来向集群提供一个共享文件系统,但是如果这个共享服务器出问题的话,将会导致它所提供的文件系统不可用。在 11hR2之前,Oracle有集群文件系统(Oracle Cluster File System,OCFS),但是它只能在Windows和UNIX/LINUX上使用。你也可以使用一些经过Oracle认证的第三方的集群文件系统。 Oracle在11gR2开始提供了自动存储管理集群文件系统(ACFS),这个集群文件系统让cooked文件系统的优点延伸到了集群环境中。 你的数据库所组成的文件可以使用上述文件系统中的任意组合,Oracle不会限制你只能选其中的一个。 Oracle 数据库中的存储层次体系: 数据库由一个或多个表空间构成。表空间是Oracle中的一个逻辑存储容器,位于存储层次体系的顶层,包括一个或多个数据文件。 1.段 段是表空间中主要的组织结构。段就是占用存储空间的数据库对象,如表、索引以及回滚段等。你在创建表时,会创建一个表段。你在创建分区表时,创建的不是表的段,而是为每个分区创建一个段。 当你创建一个索引时,就会创建一个索引段,以此类推。占用存储空间的每个对象最后都会存储在一个段中,除了刚才介绍的那些类段外,还有回滚段、临时段以及聚簇段等。 2.区段: 段本身又由一个或者多个区段组成。区段是文件中一个逻辑上连续分配的空间。(一般来讲,文件本身在磁盘上并不是连续的,否则根本就不需要消除磁盘碎片的工具了!另外利用诸如独立磁盘冗余阵列之类的 磁盘技术,你可以会发现,一个文件不仅在一个磁盘上不连续,还有可能跨多个物理磁盘)传统的每个段都至少有一个区段,自11gR2 起Oracle引入了“延迟”段的概念——当你创建对象时,Oracle不会立即为段分配 一个新的区段,只有当数据真正写入到这个对象时(insert语句),Oracle才会为这个段分配第一个区段。如果一个对象初始区段不足以容纳新增的数据,Oracle就会再为他分配另一个区段。第二个区段不一定在 磁盘位置上紧挨第一个区段,甚至有可能不在第一个区段所在的文件中。第二个区段可能与第一个区段相距甚远,但是区段内的空间总是文件中的一个逻辑连续空间。区段的大小可能不太,可以是Oracle数据块, 也可以是达到2GB。 3.块: 区段又进一步由块组成。块是Oracle中最小的空间分配单位。行数据、索引条目或临时排序结果就存储在块中。通常Oracle从磁盘读写的就是块,Oracle中块的常见大小有4种:2KB、4KB、8KB、16KB(尽管 在某些情况下32KB也是允许的,但是操作系统可能对最大大小有限制)。 一个段由一个或多个区段组成,区段则由连续分配的一些块组成。 数据库之所以允许有多个块大小,是为了实现一个名为“传输表空间”的功能。DBA可以利用这个功能来从一个数据库移动或复制已经格式化好的数据文件,把它放在另一个数据库中。例如一个数据库使用的块为 2KB,另一个数据库是8KB,如果一个数据库中不支持多种块大小,就无法传输这些信息。 在表空间内部,所有的块大小都是一致的。对于一个多段对象,如一个包含LOB列的表,可能会将这些段存放在不同的表空间(块大小可能不一样)中,但是段内块的大小一定是相同的。 块的基本格式: 1.首部:包含块类型的有关信息(表块、索引块等)、块上发生的活动事务和过去事务的相关信息(仅事务管理的块有此信息,例如临时排序块就没有事务信息)、以及块在磁盘上的地址(位置) 2.表目录:会记录在这个块上存储数据的所有的表(可能一个块存储了多个表的数据) 3.行目录:包含块中行的描述信息,这是一个指针数组,指出块中数据部分中的行。 4.空闲空间:未存放数据的空间 5.数据:已经存放数据的空间 6.尾部:标识块结束 4.表空间: 表空间是一个容器,其中包含段。每个段都只属于一个表空间。一个表空间中可能有多个段。一个段的所有区段都在段所属的表空间中。段绝不会跨越表空间边界。表空间本身可以有一个或多个数据文件。一个 区段仅会存放在一个数据文件中。不过段可以有来自多个不同数据文件的区段。 5.存储层次体系小结: 1.数据库由多个一个或者多个表空间组成 2.表空间由一个或者多个数据文件组成。这些文件可以是文件系统中的文件、裸分区、ASM管理的数据库文件】或集群文件系统上的文件。表空间包含段。 3.段由一个或者多个区段组成。段不能跨表空间存放,但是段中的数据可以跨文件(当然,隶属于同一个表空间)存放。 4.区段是磁盘上一组逻辑连续的块。区段只能在一个表空间中,而且总是在该表空间内的一个文件中。 5.块是数据库中最小的分配单位,也是数据库使用的最小I/O单位。 6.字典管理和本地管理的表空间: 表空间是如何管理区段? 在Oracle 8.1.5之前是通过数据字典,相应的这种表空间叫字典管理的表空间。所有的空间管理维护操作都是在数据字典表中,如果要存储新的数据则增加(更新、删除)一条数据。但是这个的缺点是不能 并发的处理而且查询需要更新、删除的数据的SQL效率(递归SQL)太慢。 在7.3版本中,Oracle引入了一个真正的临时表空间概念,这是一个新的表空间类型,专门用于存储临时数据,从而缓解上面问题。临时表空间中不能参加持久对象,这是它与持久的表空间最基本的区别, 此外它的空间管理还是通过数据字典表完成的。不过一旦在临时表空间分配了一个区段,系统就会一直持有(不会回收空间)。下次有人向临时表空间申请空间时,Oracle会在已分配区段列表中查找空闲 的区段。如果找到就重用,否则还是分配一个区段。这样就不用执行代价昂贵的递归SQL。 在Oracle 8.1.5及以后版本中,Oracle引入了本地管理表空间概念。这个本地管理表空间采用了与Oracle7.3中对临时表空间的相同的方法来管理空间,这样就无需使用数据字典来进行维护。本地管理表空间 会在每个数据文件中使用一个位图来管理区段,如果得到一个区段,系统所做的只是在位图中将某一位设置为1,要释放一些空间,系统再把这一位设置为0。相比于字典方式的处理,本地管理表空间在分配 和释放空间的速度就快很多。这样我们的空间分配操作就从数据库级别的串行长时间操作(递归SQL)变成了表空间级别的串行短时间操作了。 临时文件: 是一种特殊类型的数据文件。当内存不足时,Oracle 会使用它来存储一些临时文件,比如说一些比较大的排序或散列操作的中间结果、临时表中的数据以及结果集数据。 自12c起,对临时表的操作所产生的undo也会放到临时表空间中,而在以前的版本中,这部分undo是放在undo表空间中的,因而会联动产生redo。 Oracle以一种特殊的方式处理临时文件。一般而言,你对数据的每个修改都会存储在重做日志中。这些事务日志会在以后某个时间重新应用以“重做事务”,例如,数据库实例失败后进行恢复时就可能需要“重做事务”。 临时文件不包括在这个重新应用过程内,对临时文件内的数据的修改不会生成重做日志,由于undo总是受redo的“保护”(undo数据就像是表数据或索引数据一样,对undo的修改会生成一些redo,而这些redo会记入日志) 因此,这就会生成使用临时表的重做日志, 关于“真正的”临时文件:如果操作系统允许创建的话,临时表空间的文件则会以稀疏的方式创建,也就是说,这样的文件会“按需”占用磁盘的空间。即创建临时文件时,是不会占用实际的磁盘空间,只有当你使用到的 时候才会开始写入磁盘。 控制文件: 是一个相当小的文件(极端情况下能增长到64MB),它存储了数据库需要的一些文件的位置。数据库启动时,实例会从参数文件中知道控制文件的位置,而通过控制文件则会知道数据库的数据文件和在线操作日志 文件的位置。控制文件还记录了Oracle的其他一些信息,如检查点的有关信息,数据库名(与db_name参数一致)、数据库创建的时间戳、归档重做日志的历史(有时这会让控制文件变大)以及RMAN信息等。 控制文件应该通过硬件多路保存,如果硬件条件不支持,则要通过Oracle自身多路保存。每个数据库都应该有多个控制文件的副本,而且它们应该保存在不同的磁盘上,以防止出现磁盘故障而丢失控制文件。 重做日志文件: 它是数据库的事务日志。通常情况下重做日志是用于恢复的,但也可以用于: 1.系统崩溃后的实例恢复 2.从备份复原出来的数据文件的介质恢复 3.备用数据库处理 4.Streams和Golden Gate,这2个工具可以对重做日志进行挖掘,从而实现信息共享 5.让管理员能够通过Oracle LogMiner 功能查看数据库的历史事务 重做日志文件的主要目的是,当实例或存储介质出问题时,他就能派上用场。它也可以用于维护备用数据库从而实现故障时的切换。 你在Oracle中完成的几乎所有操作都会生成redo,并写入到在线重做日志文件中。当你向表中插入一行时,这一行也会写入到重做日志文件中。当你删除一行时,则会在重做日志文件中记下“删除”这个消息。 在线重做日志: 每个Oracle数据库都至少有2个在线重做日志文件组,每个重做日志组都包含一个或多个重做日志成员(redo按成员组来管理)。在同一个组内的重做日志文件是完全一样的镜像。这些在线重做日志文件的 大小是固定的,并以循环方式使用。例如,Oracle先写日志文件组1,当这组文件写满之后,会切换到日志文件组2,它会覆盖原来的内容。当日志文件组2填满时,Oracle会再切换回到日志文件组1(假设只有 2个重做日志文件组)。 从一个日志文件组切换到另一个日志文件组的动作称为日志切换。在这里重点注意:如果数据库配置不当,日志切换可能会导致整个系统临时性“暂停”。由于重做日志最重要的目标是在实例失败之后我们能 够恢复已经提交的事务,所以我们必须要保证,日志切换时所覆盖的重做日志中的内容不能包含实例所需要恢复的数据。 数据库高速缓存是临时存储数据库块的地方,这是Oracle SGA中的一个结构。当数据库从磁盘读取数据时,它会将数据库存储在这个区域中,这样以后就不必再去磁盘重新读取它们。设计数据库高速缓存主要是为了 提高性能,相对于内存I/O,物理磁盘的I/O操作速度很慢,有了数据库高速缓存,数据库就可以优化对磁盘的I/O操作。当数据块修改块(比如说更新块的上的一行)时,这些修改会在内存中完成,它会修改高速 缓存内的数据库,于此同时,数据库也会把重做这些修改所需的信息保存在重做日志缓冲区中,这是另一个SGA的数据结构。当用户提交时(commit),Oracle不会将SGA中修改的所有块写到磁盘上,它只是把重做 日志缓冲区的内容写到在线重做日志中。 在用户提交后,如果这时数据库主机突然断电,数据库高速缓存就会被彻底清空。如果发生这种情况,那么我们只能从在线重做日志文件中找到用户所做的修改。总之,修改的数据块再被保存到磁盘上的数据文件 之前,记录这个修改动作的在线重做日志文件是不能够被重用(覆盖)的。 这时数据库块写入器(DBWn)就开始登场了,DBWWn是Oracle的后台进程,它会在数据库高速缓存将满时与其他进程一起腾出一部分空间,它的另外一个作用就是检查点。检查点这个动作就是把脏块(已修改的块) 从数据库高速缓存中写至磁盘。 重做日志文件的大小和数目需要考虑问题: 1.高峰负载:你可能希望系统在业务高峰期是时候不会发生检查点的等待事件。这时你的重做日志的大小不应该以支撑“平均”的每小时业务吞吐量为目的,而应该以业务高峰吞吐量为基准。 2.大量用户修改相同的块:如果大量用户都要修改相同的块,你可能需要将重做日志文件设置的大些。因为每个人都在修改相同的块,最好尽可能多的更新之后才将其写入到磁盘。每个日志切换都会导致一个 检查点,索引你可能需要避免频繁的切换日志。不过这样一来又会影响恢复时间。 3.平均恢复时间:如果必须确保实例恢复要尽快的完成,即便是大量用户修改相同的块,我们也可能倾向于使用较小的重做日志文件。 归档重做日志 Oracle数据库可以采用2种模式运行:归档模式和非归档模式。这2种模式的区别在于Oracle重用重做日志文件时会发生什么动作,“会保留redo的一个副本吗?还是Oracle会将其重写,而永远失去原来的日志?” 如果没有以归档模式运行,磁盘出现问题,有2个选择: 1.删除与故障磁盘相关的表空间。只要表空间由一个改磁盘上的文件,就要删除这个表空间(包括表空间的内容)。如果影响到system表空间(Oracle的数字字典)或其他像UNDO这种重要的数据库相关的 表空间,那就不能用这个办法了。 2.恢复到数据备份的时间点,这个时间到故障的时间段的工作白做了。 以归档模式运行: 1.再找一个磁盘 2.从上一个数据备份的时间点把那些受影响的数据文件复原到这个磁盘上 3.对这些数据文件应用备份之后的归档重做日志,以及在线重做日志 密码文件: 不是数据库运行时必须要有的文件,它用于远程sysdba或管理员来访问数据库。 安装Oracle数据库的过程中我们需要指定“管理员组”。在UNIX/Linux上,这个组一般默认为DBA,在Windows上则默认为ORA_DBA,实际上这个组可以是操作系统上任意有效的组。这个组很“特殊”,因为这个组 中的任何用户都可以作为sysdba连接Oracle,并且无需指定用户或密码。 密码文件保存了一些用户名和密码,通过这些用户名和密码,sysdba用户可以通过网络远程进行登录。Oracle必须使用这个文件来认证sysdba用户,而不能通过数据库中存储的那些普通的用户名/密码。 修改跟踪文件: 也不是数据库运行时必须要有的文件,它是Oracle Database 10g 企业版中新增的一种文件,Oracle会在这个文件中记录自上一个增量备份以来哪些块已被修改。恢复管理器会使用这个文件来备份那些有变化的数据块, 而不必读取整个数据库。 闪回日志: 闪回日志是 Oracle Database 10g 中为支持 FLASHBACK DATABASE 命令而引入的,也是 Oracle Database 10g 企业版的一个新特性。闪回日志包含数据块“被修改之前的映象”,它用于将数据库返回(恢复)到之前 某个时间点的状态。 闪回数据库: 在以前的版本中,如果想把数据库恢复到以前的某个时间点去需要一个很耗时并且复杂的流程,引入 FLASHBACK DATABASE 这个命令之后,我们可以大大简化并加快这个流程。 这个命令没有之前: 1.DBA要关闭数据库 2.DBA(一般)要从磁盘机复原最近的一个完整的数据库备份,这通常比较耗时。通常我们得用RMAN命令:RESTORE DATABASE UNTIL <某个时间点> 3.如果归档日志不是放在数据库的服务器上,那么DBA可能还要复原最近的一个备份之后所生成的全部归档重做日志 4.接下来DBA要前滚数据库,这个过程通常要对复原出来的备份应用归档以及在线重做日志,并要在DROP USER 这个动作之前停止应用。 5.要以 RESETLOGS 方式打开数据库 这个过程很麻烦,步骤很多,通常需要花费很多时间(期间无法访问数据库) Oracle Database 10g 企业版之后: 1.DBA要关闭数据库 2.DBA启动并装载数据库,接下来使用 FLASHBACK DATABASE 命令,命令中指定的时间点可以为SCN(这算是Oracle内部的一个时钟)、复原点(SCN的一个指针)或者时间戳,这些时间应当精确到秒。 快速恢复区: Oracle数据库使用快速恢复区来管理与数据库备份和恢复相关的文件。在这块区域中,你可以找到: 1.RMAN的备份片(全量或增量备份) 2.RMAN所做的镜像备份(数据文件及控制文件的镜像备份) 3.在线重做日志 4.归档重做日志 5.多路的控制文件 6.闪回日志 DMP文件(EXP/IMP文件): 这个文件中会包含所有导出对象相关的元数据(CREATE 和 ALTER 语句形式)以及数据本身,由此我们可以重建表、模式甚至整个数据块。 DMP具有向前兼容性,新版本可以兼容旧版本。 DMP文件是平台独立的,无论导出数据的库在哪个平台上,你都可以放心将其传达到另外一个平台上导入。 DMP是二进制文件,也就是说你不能编辑修改这些文件。 数据泵文件: 这些文件由oracle数据泵expdp导出进程生成,并由数据泵impdp导入进程使用。外部表也可以创建和使用这种文件格式。 是跨平台的二进制文件,数据泵中的元数据以XML方式来存储。 平面文件: 平面文件就是一个每一“行”都是一条 “记录”的简单文件,在每行之间都有一些定界的符号,通常用逗号或管道符号(竖线)分隔。通过使用数据加载工具如SQLLDR或外部表,Oracle可以很容易的读取平台文件。

 

第四章 - 内存结构

内存结构: 系统全局区(SGA):这个是一个大的共享内存段,几乎所有Oracle进程都要访问这个区域 进程全局区(PGA):这是一个进程或线程专用的内存,其他进程/线程不能访问 用户全局区(UGA):这个内存区与特定的会话相关联。它可能在SGA中分配,也可能在PGA中分配,这取决于是用共享服务器还是专用服务器来连接数据库。如果使用共享服务器,UGA就在SGA中分配(因为任何一个共享 服务器进程都能读写你的会话的数据),反之在PGA中分配, Oracle的内存管理有5种方法: 1.自动内存管理:它是针对SGA和PGA的管理,并且只能应用在Oracle Database 11g 或更高的版本上,这种模式下 DBA 只需要设置一个参数 MEMORY_TARGET 来指定数据库使用内存的目标,数据库将自行判断每个 内存区域的大小。 2.自动SGA内存管理:它是针对SGA的管理,这种模式下DBA通过设置参数 SGA_TARGET 来指定整个SGA的目标大小。 3.手动SGA内存管理:它是针对SGA的管理,DBA可以手动设置DB_CACHE_SIZE、SHAARED_POOL_SIZE 等参数来调整SGA中每个区域的大小 4.自动PGA内存管理:它是针对PGA的,DBA可以通过设置参数 PGA_AGGREGATE_TARGET 来指定整个PGA的目标大小 5.手动PGA内存管理:它是针对PGA的,DBA可以手动设置 SORT_AREA_SIZE 、HASH_AREA_SIZE 等参数来调整PGA中每个区域的大小。Oracle强烈建议不用使用该方法。 进程全局区和用户全局区: PGA是特定于进程的一段内存,是操作系统中某个进程或线程专用的内存,不允许系统中的其他进程或线程访问。PGA一般是通过C语言运行时调用malloc() 或 memmap()来分配,而且可以在运行时动态扩大(或收缩)。 PGA绝对不会在Oracle的SGA中分配,而总是由进程或线程为自身分配。PGA中的P代表Process (进程)或Program (程序),是不共享的。 UGA实际上来说,就是你的会话的状态,它是一段你的会话一直能访问到的内存。UGA的分配位置完全取决于你如何连接Oracle。如果使用共享服务器,UGA就在SGA中分配(因为任何一个共享服务器进程都 能读写你的会话的数据),反之在PGA中分配。 所以,PGA包含进程内存,还可能包含UGA。PGA内存中的其他区域通常用于完成内存中的排序、位图合并以及散列。可以肯定的说,除了UGA,这些区域在PGA中占比最大 从Oracle Database 9i Release 1 起,有2种方法来管理PGA中的这些非UGA内存。 1.手动PGA内存管理,采用这种方法时,你要告诉Oracle数据库,如果一个进程中需要排序或散列,允许使用多少内存来完成这些排序或散列 2.自动PGA内存管理,这要求你告诉Oracle数据库,PGA在系统范围内可以尝试使用多少内存。 从Oracle Database 11 Release 1 起,自动PGA内存管理可以使用以下2种技术实现 1.通过设置 PGA_AGGREGATE_TARGET 初始化参数,告诉Oracle数据库在实例范围内PGA可以尝试使用多少内存 2.通过使用 MEMORY_TARGET 初始化参数,告诉Oracle数据库实例应当允许SGA和PGA总共使用多大内存。数据库自己将根据这个参数确定合适的PGA大小。 PGA内存管理方法受数据库初始化参数 WORKAREA_SIZE_POLICY 的控制,而且可以在会话级别进行修改。Oracle Database 9i Release 2 及更高版本中,这个初始化参数默认为AUTO,表示自动PGA内存管理, 而在 Oracle Database 9i Release 1 中,这个参数的默认设置为 MANUAL。 手动PGA内存管理: 如果采用手动PGA内存管理,除了你的会话为PL/SQl中的表和其他变量分配的内存之外,以下参数对PGA大小的影响最大 1.SORT_AREA_SIZE:在排序信息被交换到磁盘之前,所使用的内存总量(磁盘是指用户指定的磁盘上的临时表空间) 2.SORT_AREA_RETAINED_SIZE:排序完成后用于保存已排序数据的内存总量。也就是说,如果SORT_AREA_SIZE是512KB且SORT_AREA_RETAINED_SIZE是256KB,那么服务器进程最初处理查询时会用512KB 的内存对数据进行排序。等到排完序时,排序区会“收缩”为256KB,这256KB内存中放不下的已排序数据会写出到临时表空间 3.HASH_AREA_SIZE:服务器进程在内存中存储散列列表所用的内存量。通常在做一个大数据集与另一个数据集的连接时,会进行散列连接并使用到这部分内存。2个数据集中较小的一个会被散列到内存中, 内存中的散列区中放不下的部分会通过连接键存储在临时表空间中。 在使用*_AREA_SIZE 参数时,需要记住以下重点的几点: 1.这些参数控制这一个排序、散列或位图合并操作所使用的最大内存量 2.一个查询可能包含多个操作,这些操作可能都要使用这部分内存,并且会创建多个排序/散列区。同时还要记住,你也可以同时打开多个游标,每个游标都有自己的 SORT_AREA_RETAINED 需求。所以,如果把 排序区大小设置为10MB,在会话中实际上可能会用到10MB,100MB、1000MB或更多内存。设置这些参数并非是对整个会话的限制,它们只是对会话中的一个操作进行限制。而在你的会话中,一个查询可以有 多个排序,或者多个查询需要用到一个排序。 3.这些内存区都是根据需要来分配的。如果像我们之前做的一样,将排序区大小设置为1GB,这并不是说你实际分配了1GB的内存排序区,而是说,你允许Oracle进程为一个排序/散列操作最多分配1GB的内存 自动PGA内存管理: 引入自动PGA内存管理是为了解决以下问题: 1.易用性:很多人并不清楚如何设置适当的 *_AREA_SIZE 参数 2.手动分配是一种“以一概全”的方法:一般来说,随着在一个数据库上运行类似应用的用户数的增加,排序和散列所用的内存量也会呈线性增长。如果有10个并发用户,每个用户排序区大小为1MB,这就会使用 10MB的内存。除非DBA能一直坐在数据库控制台前不断的为每个用户调整排序/散列区的大小,否则每个用户会一直使用同样的设置值。随着允许使用的内存量的增加,对磁盘上的临时表空间执行的物理 I/O在减少。查询的响应时间也会缩短。手动分配方式会把排序所用的内存量固定为一个常量,而不考虑实际上有多少内存是可用的。利用自动内存管理的话,我们可以使用所用可用的内存,数据库会 根据工作负载动态的调整实际我们可以使用的内存量 3.内存控制:根据上一条,手动分配很难(或许说不可能)控制Oracle实例在合理范围内使用内存。你不能控制实例要用的内存总量,因为你根本无法控制会发生多少并发的排序和散列。所以很有可能导致 数据库尝试使用的内存量超过了服务器所拥有的物理空闲内存 PGA内存管理: 首先简单的建立SGA并确定其大小。SGA是一段固定大小的内存,所以可以看到它的准确大小,这也将是SGA的总大小(除非你做了调整)。确定了SGA的大小后,再告诉Oracle数据库PGA的大小:“你总共有这些 内存来分配所有的工作区(排序区和散列区的一个新总称)”。理论上如果你有一台2G物理内存的服务器,可以分配768MB内存给SGA,768MB给PGA,余下的512MB内存留给操作系统和其他进程。 建立自动PGA内存管理时,需要为2个数据库实例初始化参数确定适当的值 1.WORKAREA_SIZE_POLICY:这个参数可以设置为 MANUAL 或者 AUTO 。如果是 MANUAL ,会使用排序区和散列区大小参数来控制分配的内存量。如果是 AUTO ,分配的内存量会在PGA内存中自动的变化。 默认值为AUTO,这也是推荐的设置。 2.PGA_AGGREGATE_TARGET:这个参数会控制在数据库实例上为所有工作区(即所有的排序区和散列区)分配的内存总量。在不同的数据库版本中,这个参数的默认值有所不同,还可以用多种工具来进行设置。 一般来说,如果使用自动PGA内存管理,就应该明确的设置这个参数 如何自动分配内存: 文档中没有说明采用自动模式时分配内存的算法,而且在不同版本中这个算法还可能(而且将会)不断的改变。所以只要技术以A开头(表示自动,Automatic),那就代表你会丧失一定的控制权,而由底层 算法自行确定做什么以及如何进行控制。 可以根据一些例子来分析: 1.PGA_AGGREGATE_TARGET是一个上限目标,而不是启动数据库时预分配的内存大小。你可以把 PGA_AGGREGATE_TARGET 设置为一个超大的值(远远大于服务器上实际可用的物理内存量),你会看到, 在数据库启动时并没有真的分配很大内存给PGA 2.某个具体的会话所能使用的PGA内存量也依赖与 PGA_AGGREGATE_TARGET 的设置。相关算法所能确定的一个进程可用内存的最大值,这会随数据库版本而变化。通常情况下,一个进程分配到的PGA可用内存 量是根据可用内存总量和正在争用内存的进程数来确定的 3.随着服务器上工作负载的增加(越来越多的并发查询和并发用户),分配给各个工作区的PGA内存量会减少,数据库会努力保证所有PGA分配的总和不超过 PGA_AGGREGATE_TARGET 设置的阈值。 使用 PGA_AGGREGATE_TARGET 控制内存分配: 之前我曾说过,“理论上”可以使用 PGA_AGGREGATE_TARGET 来控制实例使用的PGA内存的总量。不过,从上一个例子中可以看到,这并不是一个硬性限制,数据库实例会尽力尝试将PGA的内存总用量 保持在 PGA_AGGREGATE_TARGET 限制以内,但是如果实在无法限制住,它就会被迫超量使用来保证数据库的运行(数据库实例无法预估一个语句所要占的内存,如果超过了也要保证运行,除非超过系统 内存总量,会抛出错误)。 说它是“理论上”的限制还有另外一个原因:尽管工作区在PGA内存中所占的比重很大,但PGA内存中并非只有工作区。PGA内存分配涉及到很多方面,其中只要工作区在数据库实例的控制之下。 如何选择手动和自动内存管理: 默认情况下,倾向于自动PGA内存管理。 PGA和UGA小结: PGA是进程专用的内存区,这是Oracle专用或共享服务器需要的一组不依赖于会话的变量。PGA是一个内存“堆”,其中还可以分配其他结构。UGA也是一个内存堆,其中定义了属于会话的结构。如果使用专用服务器连接 数据库,UGA会从PGA中分配,如果使用共享服务器连接,UGA则从SGA中分配。这表明,使用共享服务器时,必须适当的设置SGA中大池的大小,以便有足够的内存空间来应对每一个数据库并发用户。所以,相比于使用 专用服务器连接方式,如果使用类似配置的共享服务器连接方式,需要设置更大的SGA。 系统全局区: 每个Oracle数据库实例都有一个很大的内存结构,称为系统全局区(SGA)。这是一个大型的共享内存结构,每个Oracle进程都会访问它。SGA的大小不一,在小型测试系统上可能只有几十MB,在中大型系统上可能呢有 几个GB,对于巨型系统,则可能多大几百GB。 SGA内分为多个不同的池。 1.java池:java池是为数据库中运行的java虚拟机(JVM)所分配的一段固定大小的内存。在 Oracle Database 10g 及更高版本中,java池可以在数据库启动和运行时动态调整大小。 2.大池:在使用共享服务器连接时存放会话内存(UGA)、在使用并行执行功能时作为消息缓冲区、在RMAN备份时作为磁盘O/I缓冲区。大池可以动态调整 3.共享池:共享池包含共享游标、存储过程、状态对象、字典缓存和诸如此类的大量其他数据。在 Oracle Database 9i 及更高版本中,共享池可以动态调整大小 4.流池:这是一个专门针对数据传输/共享工具,(例如Oracle GoldenGate和Oracle Streams)的内存池。它是 在 Oracle Database 10g 中新增的,可以动态调整大小。如果未配置流池,但是使用流功能, Oracle会使用共享池中最多10%的空间作为流内存 5.空池:这个池其实没有名字。它是缓冲区(用来缓存数据库块)、重做日志缓冲区和“固定SGA”区专用的内存 对SGA整体大小影响最大的参数: 1.JAVA_POOL_SIZE:控制java池的大小 2.SHARED_POOL_SIZE:在某种程度上控制共享池的大小 3.LARGE_POOL_SIZE:控制大池的大小 4.STREAMS_POOL_SIZE:控制流池的大小 5.DB_*_CACHE_SIZE:共有8个 CACHE_SIZE 参数,控制各个可用的缓冲区缓存的大小 6.LOG_BYFFER:在某种程度上控制重做日志缓冲区的大小 7.SGA_TARGET:在 Oracle Database 10g 及更高版本中用于SGA自动内存管理,可用动态调整 8.SGA_MAX_SIZE:用于控制SGA大小 9.MEMORY_TARGET:在 Oracle Database 11g 及更高版本中用于自动内存管理(包括PGA和SGA的自动内存管理) 10.MEMORY_MAX_TARGET:在 Oracle Database 11g 及更高版本中对于采用自动内存管理的数据库,使用PGA和SGA的内存总用量尽可能达到(并且不超出)MEMORY_MAX_TARGET所设定的值,这实际上只是一个目标值。 如果用户数超过了某个水平,或者会话分配了超大且不能调整的内存,PGA还是可能会超出这个最优值的。 固定SGA区: 固定 SGA 区是SGA的一个组件,其大小因平台和版本而异。安装时,固定SGA区会“编译到” Oracle可执行文件本身当中(所以说它是“固定”的)。在固定SGA区中,有一组指向其他组件的变量,还有一些包含了各个 参数值的变量,我们无法控制固定SGA区的大小,不过通常情况下它很小。可以把它理解为SGA的“启动”区,Oracle在内部要使用这个区来找到SGA内的其他一些东西 重做缓冲区: 数据需要写到在线重做日志中时,在它们被写至磁盘之前,需要在重做缓冲区中临时缓存这些数据,由于内存到内存之间的传输比内存到磁盘的传输要快很多,因此使用重做日志缓冲区可以加快数据库的操作。 数据在重做缓冲区里停留的时间不会太长。实际上,LGWR 进程会在以下任何一种情况发生时把缓冲区数据刷新输出到磁盘: 1.每3秒一次 2.发生提交(commit)或回滚(rollback)请求时 3.要求LGWR切换日志文件时 4.重做缓冲区用满1/3,或者缓存重做日志数据达到1MB时 块缓冲区缓存: 很大的组件:块缓冲区缓存。oracle将数据块写至磁盘之前,或从磁盘上读取数据块之后,就会把这些数据块块存储在块缓冲区缓存中。对我们来说,这是SGA中一个很重要的区域。如果块缓冲区缓存太小, 我们的的查询就会永远也执行不玩,如果太大,又会抢占其他进程的资源(例如,因为没有为专用服务器连接留下足够的空间来创建其PGA,导致无法连接使用数据库) 在Oracle数据库较早版本中,只有一个块缓冲区缓存,从任何段上读来的块都放在这个区中。从Oracle 8.0 开始,可以把各个段上的已缓存块放在SGA中的3个位置上 1.默认池:所有来自各个段中的块一般都在这个池中缓存,这就是上面提到 的较早版本那个唯一的块缓冲区缓存 2.保留池:当一些被频繁访问的段放在默认池中后,它们会因为其他段对块缓冲区缓存的需求而老化退出默认池,如果你希望能将这些被频繁访问的段也尽量保留在块缓冲区缓存中,按惯例应该使用保留池 3.回收池:与保留池相反,当你很随机的访问一些大型的段时,如果这些段放在默认池(保留池)中,会导致其他常用且频繁访问块被刷出缓冲区缓存。另外缓存这样大型段也没有什么意义,因为等你想要 再次访问这个块时,它可能已经老化退出了缓存。所以要把这种段与其他段分开,这样就不会导致真正需要留在默认池和保留池中的块老化退出缓存,这种情况按惯例应该使用回收池 分成3个池的目的是让DBA能把段分成“热”区、“温”区和“不适合缓存”区。理论上讲,默认池中的对象应该足够热(被访问的足够频繁),而能否留在缓存中也完全取决与此。还有一些段比较热门,但是还算不上 很“热”的话,它们就称作“温”块,这些段可能会从缓存中被刷出去,为一些不常用的块(“不适合缓存”的块)腾出空间。为了保持这些“温”块能留在缓存中,可以采取下面的某种做法: 1.将这些段分配到保留池,力图让温块在缓冲区缓存中停留的更久 2.将“不适合缓存”段分配到回收池,并将回收池设置的很小,以便能快速的进入缓存和离开缓存(减少内存管理的开销) 无论哪种做法都会增加DBA的工作量,因为有3个缓存池,要考虑它们的大小,还要考虑将那些对象分配到这些缓存池中。另外请记住这次池之间是没有共享的。总之,这些池一般被视为一种非常精细调优级别很低 的设备,只要大部分的其他调优手段都被用过之后才应考虑对这些池进行调优 在缓冲区缓存中管理块: 为简单起见,我们这里假设只有一个默认池(其他池都是以相同的方式管理) 缓冲区缓存中的块实质上在一个区域上管理,但有2个不同的列表指向这些块 1.脏块列表,其中的块需要由数据库块写入器(DBWn)写入磁盘 2.非脏块列表 在 Oracle 8.0 及以前的版本中,非脏块列表用的是一种最近最少使用的列表。所有的块按被使用的顺序列出。在Oracle 8i 及以后的版本中,这个算法有所修改。Oracle 不再按物理顺序方式来维护块列表, 而是采用了一种叫作接触计数的算法,如果你的数据库操作命中缓存中的某个块,则会增加与之关联的计数器的值。但这不是说每次命中这个块都会增加计数,而是大约每3秒一次(如果连续命中的话)。 Oracle会有意的使块“冷却”,过段时间会让计数递减。以上算法都是为了让频繁使用的块被缓存而不常使用的块不会缓存太久。 多种块的大小: 从 Oracle Database 9i 开始,同一个数据库中可以有多种不同的块大小,你可以有一个“默认的”块大小,以及最多4种其他的块大小。每种不同的块大小都必须有其自己的缓冲区缓存。默认池、保留池和 回收池只能缓存具有默认块大小的块。为了在数据库中使用非默认的块大小,需要配置相应的缓冲区池来保存这些块。 默认池、保留池和回收池对于缓冲区缓存的精细调优来说应该以及足够了,多种块大小主要用于从一个数据库向另一个数据库传输数据。 共享池: 是Oracle缓存一些“程序”数据的地方。在解析一个查询时,解析得到的结果就缓存在那里。在完成解析整个查询任务之前,Oracle会搜索共享池,看看这个工作是否以及完成。你运行的PL/SQl代码也在共享池中 缓存,还会在这里共享。 共享池的特点是有大量的内存块,一般为4KB或更小。共享池的内存根据最近最少使用(LRU)原则来管理。在这方面,它类似于缓冲区缓存,如果你不用某个对象,它就会被共享池踢出。 共享池如果太小,会严重影响性能,甚至导致系统看上去像中止一样,如果共享池太大,也会有同样的问题(与管理一个较小的满的共享池相比,管理一个更大的满的共享池需要做的工作更多) 大池: 它被用于大块的分配(如共享服务器中的UGA),而共享池无法处理这么大的内存块。 大块内存分配是得到一块内存后加以使用,然后就到此为止,没有必要缓存这个内存,是一个回收型的内存空间,而共享池则是更像是保留缓冲区池(如果对象可能会被频繁的使用,就将其缓存起来) java池: 目的是支持在数据库中运行java,如果用java编写一个存储过程,Oracle会在处理代码时使用java池的内存。 java池有多种用法,这取决于服务器运行的模式。如果采用专用服务器模式,java池包含每个java类的共享部分,由每个会话使用。这实际上是只读部分(执行向量、方法等),每个类的共享部分大约为4-8KB。 因此,采用专用服务器模式时,java池所需的总内存相当少,可以根据要用的java类的个数来确定。 如果使用共享服务器模式的话,java池中包含以下部分: 1.每个java类的共享部分 2.UGA中用于各会话状态的部分,这是从SGA中的java池中分配的。UGA中余下的部分会照常在共享池中分配,或者如果配置了大池,就会在大池中分配 流池: 如果未配置流池,但是使用流功能,Oracle会使用共享池中最多10%的空间作为流内存 Oracle的产品用流池来缓冲队列消息,这些功能原来是使用基于磁盘的持久的队列,而且附带一些自身的开销,现在它们改用内存中的队列了。 SGA内存管理: SGA内存管理的相关配置分为2类: 1.可以自动调优的SGA参数:目前这些参数有 DB_CACHE_SIZE、SHARED_POOL_SIZE、LARGE_POOL_SIZE、JAVA_POOL_SIZE和STREAMS_POOL_SIZE 2.需要手动调整的SGA参数:这些参数包括 LOG_BUFFER、DB_NK_CACHE_SIZE、DB_KEEP_CACHE_SIZE和DB_RECYCLE_CACHE_SIZE 对于SGA内存组件中那些可以自动调优的部分,有以下3种方式对它们进行管理 1.手动SGA内存管理:即手动设置必要的池和缓存参数 2.在Oracle 10g 及更高的版本中,使用自动SGA内存管理:即设置 SGA_TARGET 参数,通过设置改参数,数据库实例便能自行设置和调整SGA中的这些组件的内存大小 3.在Oracle 11g 及更高的版本中,使用自动内存管理:即设置 MEMORY_TARGET 参数,通过设置该参数,数据库实例便能自行管理SGA和PGA的内存区域

 

 

第五章 - Oracle进程

Oracle进程: Oracle中的每个进程都要执行一个特定的任务(或一组任务),每个进程都会为自己分配内存(PGA)来完成它的任务。一个Oracle实例主要有以下3类进程: 1.服务器进程:这些进程根据客户端的请求来完成工作。之前我们已经对专用服务器和共享服务器有了一定的了解,它们就是服务器进程 2.后台进程:这些进程随数据库启动而启动,用于完成各种维护任务,如将数据块写至磁盘,维护在线重做日志、清理异常中止的进程以及维护自动工作负载存储库(AWR)等 3.从属进程:这些进程类似于后台进程,它们代表后台进程或服务器进程执行一些额外的工作 服务器进程: 服务器进程就是执行客户端会话指令的进程,它们负责接收由应用发送给数据库的SQL语句,并在数据库中执行 Oracle的2种连接方式: 1.专用服务器连接:采用专用服务器连接时,会在服务器上得到一个针对这个连接的专用进程,这时,数据库连接与服务器进程之间是一一对应的 2.共享服务器连接:采用共享服务器连接时,多个会话可以共享一个服务器进程池,其中的进程由Oracle实例生成和管理。你所连接的是一个数据库调度器,而不是特意为该连接创建的专用服务器进程 专用服务器和共享服务器进程的任务是一样的:处理你提交的所有SQL。当你向数据库提交一个查询语句时,就会有一个Oracle专用/共享服务器进程来解析这个查询,把它放到共享池中(或者最好能发现这个查询已经 在共享池中)。这个进程要自行制定查询计划(如果有必要的话),然后执行它,并尽可能的在缓冲区缓存中找到必要的数据,或者将数据从磁盘读入缓冲区缓存。 这些服务器进程是“干重活”的进程,因为这些进程在执行排序、汇总、连接等工作,总之几乎所有工作都是由这些进程来完成 专用服务器连接: 你的客户端应用链接着Oracle文件,这些库文件提供了与数据库通信所需的API。这些API指定如何向数据库提交查询,并处理返回的游标。它们知道如何把你的请求打包为网络调用,而专用服务器则知道如何解包。 这部分软件被称为Oracle Net。这是一种网络软件/协议,Oracle利用这个软件来支持客户端/服务器处理(即使在一个N层体系结构中也会“潜伏”着客户端/服务器程序)。有时候,即使从技术上讲没有涉及Oracle net ,Oracle实际也采用了同样的体系来处理。也就是说,即使客户端和服务器在同一台机器上,也会采用这种双进程(也称为双任务)体系结构。这种结构有2个好处 1.远程执行:很显然,客户端应用和数据库可以在不同的机器上运行 2.地址空间隔离:服务器进程是可以读写SGA的。如果客户端进程和服务器进程物理链接在一起,客户端进程中一个错误的指针就会轻易破坏SGA中的数据结构 共享服务器连接: 共享服务器连接要求必须使用Oracle net,即使客户端和服务器都在同一台机器上也不例外。 客户端应用(其中链接了Oracle库文件)会与一个调度器程序物理的连接在一起。对于一个实例,我们可以配置多个调度器,而一个调度器对于数百个用户的情况也很常见。调度器程序只负责从客户端应用接收入站 请求,并把它们放入SGA中的一个请求队列。共享服务器进程池中第一个可用的进程会从队列中依次获取请求,并附加相关会话的UGA。共享服务器进程将会处理这个请求,并把得到的输出放在响应队列中。调度器程序 持续监视着响应队列中的结果,并把结果传回给客户端应用。 数据库常驻连接池: 数据库常驻连接池(DRCP)是连接数据库并建立会话的一种可选方法。有些应用接口自身不支持高效连接池(如PHP),DRCP就是为这种应用接口设计的一种更为高效的连接池方法。 在使用共享服务器连接时,月购买共享服务器进程会被多个会话共享,一个会话可能会使用多个共享服务器进程。但当使用DRCP时,情况就不一样了,从池中选出的专用服务器进程会在会话的整个生命周期中为 客户端进程所用。在共享服务器连接的情况下,如果要在一个会话中执行3个SQL语句,这个3个语句很可能会由3个不同的共享服务器进程来执行。但是只有DRCP时,这3个语句都将由从池中为该会话分配的专用 服务器进程来执行。这个专用服务器进程一直由这个会话专用,直到会话将它释放回进程池。 连接和会话: 在一个连接上可以建立0个、1个或多个会话。各个会话之间是独立的。一个连接上的各个会话可以属于不同的数据库用户 连接:连接是客户端到Oracle数据库实例的一条物理路径。连接可以通过网络或者IPC机制建立。通常会在客户端进程与一个专用服务器(或一个调度器进程)之间建立连接。 会话:会话是数据库实例中存在的一个逻辑实体,你所看到的会话状态信息,代表了你的会话在实例内存中的数据结构的集合。会话是你在数据库上执行SQL、提交事务和运行存储过程的地方 专用服务器、共享服务器和数据库常驻连接池 1.什么时候使用专用服务器: 因为存在一对一的映射,所以不存在担心那些长时间运行的事务会阻塞其他事务。其他事务只需要通过自己的专用服务器进程来处理。因此,在非OLTP模式下,也就是可能有长时间运行事务的情况下, 应该只考虑使用这种模式。专用服务器也是Oracle的推荐配置,它能很好的扩展,只要服务器有足够硬件资源(CPU和内存)来应对系统所需的专用服务器进程个数,专用服务器甚至可以用于数千条并发连接 2.什么时候使用共享服务器: 共享服务器是一种共享资源,而专用服务器不是。使用共享服务器时,必须当心,不要长时间独占这个资源。共享服务器的首要原则:要确保事务的持续时间尽量短,事务可以频繁的执行,但必须在短时间 内执行完(这正是OLTP系统的特点)。如果事务持续时间很长,你会发现整个系统会慢下来,因为共享资源被少数进程独占着。 共享服务器还会造成人为死锁的情况: 你有5个共享服务器进程,并建立了100个用户会话。现在,在任何一个时间点上最多可以有5个活动用户会话。假设其中一个用户会话更新了某一行,但是没有提交。 在这时,可能又有另外5个用户会话力图锁住这一行,当然,这5个会话会被阻塞,然后持有这一行锁的用户试图提交事务,但是已经没有共享服务器进程可用。 因此,由于这些原因,共享服务器只适用于OLTP系统。这种系统的特点是事务短而且频繁。 3.共享服务器的潜在好吃: 1.减少操作系统进程/线程数 2.人为的限制并发度 3.减少系统所需的内存 4.数据库常驻连接池(DRCP) DRCP可以减少进程(我们使用进程池),安全的节省内存,同时完成避免了人为死锁。但DRCP没有共享服务器的多线程功能:一个客户端进程从池中获得一个专用服务器进程后,它将拥有该进程直到 客户端进程将它释放。DRCP对某些客户端应用最为适合,例如应用需要频繁的连接数据库,进行一些相当较小的处理,再断开连接,如此反复。 5.专用/共享服务器小结: 除非你的系统负载过重,或者需要为某个特定的功能使用共享服务器,否则专用服务器可能是最适合你的。 后台进程: 数据库实例由SGA和后台进程组成。后台进程默默的执行一些维护任务来保障数据库运行。 后台进程可用分成2类: 1.由特定任务的进程 2.能够执行各种其他任务的进程(如工具进程) 特定任务后台进程: 特定任务后台进程的数量、名称和类型都因版本而异 1.PMON:进程监视器 负责在连接出现异常中止后进行清理工作。例如,一个专用服务器“失败”或者出于某种原因被结束调,就要由PMON进程负责善后(恢复或撤销工作),并释放资源。PMON会回滚未提交的工作,释放锁,并 释放之前为失败进程分配的SGA资源 除了在连接异常中断后进行清理之外,PMON还负责监视其他的Oracle进程,并在必要的时重启这些后台进程。 2.LREG:监听注册进程 从Oracle Database 12c 开始,LREG进程负责将数据库实例和服务注册到监听器中。当一个数据库实例启动时,LREG进程会去查看那个大家熟悉的端口(1521端口),看看是否已经有监听器在上面运行了。 3.SMON:系统监视器 SMON进程用来做所有“系统级”的任务,之前说的PMON所对应的是各个进程,而SMON则是从系统级的视角出发,成为了数据库上的垃圾收集器。SMON工作包括以下几种 1.清理临时表空间:由于某种原因会话异常中止了,SMON要负责清理这些临时表空间的区段。 2.合并空闲表空间:如果你在使用字典管理的表空间,SMON会负责取得表空间中互相连续的空间区段,并把它们合并为一个更大的空闲区段。 3.针对原来不可用的文件恢复活动的事务:在实例崩溃/恢复时由于某个文件不可用,可能会跳过一些失败的事务(即无法恢复),这些失败的事务将由SMON来恢复。例如,磁盘上的文件可能不可用或者 未装载,导致部分事务失败,当文件变成可用时,SMON将会恢复这些事务 4.执行RAC中失败节点的实例恢复:在一个Oracle RAC配置中,集群中的一个数据库实例失败时,集群中的另外某个节点会打开该失败实例的重做日志文件,并恢复失败节点上的所有数据 5.清理 OBJ$:OBJ$是一个底层的数据字典表,数据库中几乎每个对象(表、索引、触发器、视图等)都在其中对应一个条目。很多情况下,有些条目表示的可能是已经删除的对象,或者表示 “not there”的对象。要由SMON进程来删除这些不需要的行 6.管理撤销:SMON会负责实施撤销段(undo)的自动上下线,以及收缩撤销段 7.回滚段离线:当使用手动的回滚(rollback)段管理时,DBA可以让一个有活动事务的回滚段离线,或将其置为不可用状态。这时候有可能还有活动事务在使用已离线的回滚段。在这种情况下,回滚 段并没有真正离线,它只是标记为“离线中”,之后SMON会在后台定期尝试将其真正的离线,直至成功为止 4.RECO:分布式数据库恢复 RECO有一个非常核心的任务:由于俩阶段提交(2PC)期间的崩溃或连接丢失等原因,有些事务可能会保持在准备状态,这个进程就是要恢复这些事务。2PC是一种分布式协议,允许一个或多个不同数据库 的修改实现原子提交。它力图在提交之前尽可能的关闭分布式失败窗口,如果在N个数据库之间采用2PC,其中一个数据库(通常是客户最初登录的那个数据库)将成为协调器。这个站点会询问其他N-1个 站点是否已准备好提交。实际上,这个站点会转向到另外这N-1个站点,让它们准备好提交。如果某个站点投票说YES,称其已经准备好提交,但是在得到协调器的指令并真正提交之前,网络失败了,这个 事务就会成为一个可疑的分布式事务。这时候RECO会试图联系协调器获得对该事务的处理结果。在此之前,事务会保持未提交的状态。 5.CKPT:检查点进程 实施检查点主要是DBWn进程的工作,CKPT仅仅是协助实际运行检查点的进程,来更新数据文件的文件头。 6.DBWn:数据库块写入器 数据库块写入器是负责将脏块写入磁盘的后台进程。DBWn会写出缓冲区缓存中的脏块,这通常是为了在缓存中腾出更多的空间(释放缓冲区来读入其他数据),或者是为了推进检查点(将在线重做日志文件 中的位置前移)。块写入器进程会把块分散的写到磁盘中的各个位置,DBWn会做大量的离散写,而日志写入器(LGWR)则不同,它会在重做日志中进行大量的顺序写,这是Oracle在有了DBWn进程之外,还要 有重做日志和LGWR的原因(顺序写比离散写快很多)。 7.LGWR:日志写入器 LGWR进程负责把SGA中重做日志缓冲区的内容刷新到输出到磁盘。如果满足一些条件之一时,就会做这个工作: 1.每过3秒 2.一个提交或回滚发起时 3.LGWR被告知进行日志文件切换时 4.重做日志缓冲区1/3满,或者已经包含1MB的缓冲数据 8.ARCn:归档进程 ARCn进程的任务是:当LGWR将一个在线重做日志文件填满时,就将其复制到另一个位置。此后这些归档的重做日志文件可以用于完成介质恢复。ARCn通常将在线重做日志文件复制到其他至少2个位置(冗余 正是不丢失数据的关键所在) 9.DIAG:诊断进程 在以前的版本中,DIAG进程专用于RAC环境。从Oracle Database 11g 开始,利用ADR(高级诊断库),它会复制监视实例的整体情况,而且会捕捉处理实例失败时所需的信息。这既适用于单个实例配置,也 适用于多实例的RAC配置 10.FBDA:闪回数据归档进程 这个进程是 Oracle Database 11g Release 1 新增的,这是新增闪回数据归档功能的重要组成部分。闪回数据归档功能是指能够闪回查询很久之前的数据。这种长期历史查询功能是通过维护行的历史变化 记录,即记录一个表中的每一行数据的变化来实现的。这个历史就是由后台的FBDA进程维护的。这个进程在事务提交之后就立即工作。FBDA进程会读取该事务生成的undo,并回滚事务作出的改变。然后将 回滚后的这些行(原来的值)记录在闪回数据归档中。 11.DBRM:数据库资源管理器进程 DBRM进程会去实施那些为一个数据库实例配置的资源计划。它会设置指定的资源计划,执行相关的各种操作来实施/实现这些资源计划。资源管理器允许数据库管理员对数据库实例所用的资源、应用访问 数据库所用的资源或者单个用户访问数据库所用的资源进行细粒度的控制 12.GEN0:通用任务执行进程 GEN0为数据库提供一个执行通用任务的进程。这个进程的主要目的是分担进程中某些可能造成进程阻塞的处理进程,并把它放在后台中完成。例如,如果主ASM进程需要完成某个阻塞文件的操作,而且这个 操作是可以在后台被安全的完成的,在这种情况下,ASM进程就可以请求GEN0进程来完成这个操作,并让GEN0在完成时给出通知。本质上讲,这类似于后面的从属进程。 13.其他常见的特定任务进程 根据你所使用的Oracle数据库所拥有的特性,可能还会看到其他一些特定任务进程。这里只是简单的列出这些进程,并提供其功能的简要描述 工具后台进程: 这些后台进程是可选的,可以根据你的需要来选用。 从属进程: 1.I/O从属进程 2.并行查询从属进程 I/O从属进程: I/O从属进程用于在不支持异步I/O的系统上或设备上模拟异步I/O。 并行查询从属进程: 对于select、create table、update等SQL语句,创建一个执行计划,其中包含了可以同时执行的多个执行计划,将每个执行计划的输出合并在一起构成一个更大的结果。

 

 

第六章 - 锁和闩

    开发多用户、数据库驱动的应用时,最大的难点之一是:一方面要力争取得最大限度的并发访问,与此同时还要确保每个用户能在保证一致性的前提下读取和修改数据。为此就有了锁机制。

什么是锁:
    锁用于管理对共享资源的并发访问。数据库中使用锁是为了指出对共享资源进行并发访问,与此同时还要保证数据完整性和一致性。
    有多少种数据库,就可能有多少种实现锁的方法。如Sybase、SQL server 和 Informix,这3个数据库都为并发控制提供了锁机制,但它们的实现方式却有本质上的区别吗。
    在Oracle中你会了解到下面几点:
        1.事务是数据库的核心,它们是“好东西”
        2.应该延迟到适当的时刻才提交。不要太快的提交,以避免对系统带来压力。这是因为,即使事务很长或很大,也一般不会对系统造成压力。相应的原则是:在必要时才提交,不要提前。事务的大小
            只应该根据业务逻辑来定
        3.只要需要,就应该尽可能长时间的保持对数据所加的锁。这些锁是你能利用的工具,而不是让你退避三舍的东西。锁并不是什么稀有资源。恰恰相反,你就应该长期的保持数据上的锁,锁并不稀少,
            而且它们可以防止其他会话修改信息
        4.在Oracle中,行级锁没有相关的开销,一点都没有。不论你是有1个行级锁,还是1000000个行级锁,专用于锁定这个信息的资源数都是一样的。
        5.不要以为锁升级会“对系统更好”(例如,使用表级锁而不是行级锁)。在Oracle中,锁升级对系统没有任何好处,也不会节省任何资源。
        6.可以同时得到并发性和一致性。你可以每次都快速而准确的得到它们。数据读取器不会被数据写入器阻塞。
    
    锁的问题:
        1.丢失更新:
            以下情况按顺序执行:
            1.会话1中的一个事务获取(查询)了一行数据,放入本地内存,并显示给一个最终用户,User1
            2.会话2中的另一个事务也获取了这一行,并将数据显示给另一个最终用户,User2
            3.User1 使用应用修改了这一行,并让应用更新数据库并提交,会话1中的事务现在已经完成
            4.User2 也修改了这一行,并让应用更新数据库并提交,会话2中的事务已经完成(在步骤3之前完成)
        
            2种方式解决:
            悲观锁:
                这种模式在用户修改数值之前就已经开始生效了。例如,用户打算对他选择的且在屏幕上可见的某个特定行执行更新(比如通过点击某个按钮),改行就会被加上一个行锁。这个行锁会一直持续到应用
                程序在数据库中执行用户的修改 并提交的时候
                
                悲观锁仅适用于有状态或有连接环境。也就是说,你的应用于数据库之间有一个持续的连接,而且只有你一个人使用这条连接(至少是在你的事务的生命周期内)。这种有状态的连接代价太大。
                根据屏幕上选择的数据,应用将提供绑定变量的值,然后重新从数据库查询这一行,同时锁住这一行,不允许其他会话去更新它:悲观锁因此得名。我们在试图更新之前就把行锁住,因为我们很
                悲观,对于这一行能不能保持住未改变的状态很是怀疑

            乐观锁:
                把所有锁定的动作都延迟到即将执行更新之前才进行。换句话说,我们会修改屏幕上的信息而不需要先锁定它。我们很乐观,认为数据不会被其他用户修改。
                实现乐观并发控制的方法有很多种:
                    1.使用一种特殊的列,这个列由一个数据库触发器或应用程序代码维护,用以告诉我们行记录的“版本”
                    2.使用一个总和校验或散列,这是使用原来的数据计算得出
                
                使用版本列的乐观锁:
                    这种方法很容易实现,如果你想保护数据库表不出现丢失更新的问题,就在对应的每个表上增加一列。这一列一般是NUMBER或 DATE / TIMESTAMP 类型的列,通常通过表上的一个行触发器来维护。每次
                    修改行时,这个触发器要负责递增NUMBER列中的值,或者更新 DATE / TIMESTAMP 列。(避免使用触发器,推荐使用 DATE / TIMESTAMP)
                    
                    如果应用要实现乐观并发控制,只需要保存这个附加列的值,而不需要保存其他列的所有“前”映象。应用只需要在发起更新请求那一刻,去验证数据库中这一列的值与最初读出数据时的值是否匹配。
                    如果两个值相等,就说明这一行未被更新过。
                    需要一种方法来维护这个值,我们有2种方式:可以由应用维护这一列,更新记录时将这一列的值设置为当时的时间戳;也可以由触发器/存储过程来维护。让应用维护会比使用触发器维护的效率更高,
                    因为触发器会在修改操作之外增加额外的开销。
                    不能总是依赖各个应用来维护这个字段,原因是多方面的。首先,这样会增加应用程序的代码。此外,之后的每个应用也必须遵循这些规则。所以在这种情况下,我的建议是把整个更新逻辑封装到
                    一个存储过程中,而不要让应用直接更新表。

                使用总和校验的乐观锁:
                    这与前面的版本列的方法很类似,不过它是基于数据本身来计算得出一个“虚拟列”版本列。
                    我们可以采用相同的方法使用这些散列值或总和校验。只需要简单的将数据库中读出数据时得到的散列值或总和校验值跟修改数据前得到的散列值或总和校验值进行比较。
                    Oracle提供的内置函数:
                        1.OWA_OPT_LOCK.CHECKSUM
                        2.DBMS_OBFUSCATION_TOOLKIT.MD5
                        3.DBMS_CRYPTO.HASH
                        4.DBMS_SQLHASH.GETHASH
                        5.STANDARD_HASH
                        6.ORA_HASH
                    
                    计算散列或总和校验是一种CPU密集型操作(相当占用CPU),其计算代价很昂贵。如果系统上CPU是稀缺资源,在这种系统上就必须充分考虑到这一点。
            
        乐观锁还是悲观锁:
            悲观锁在Oracle中工作的很好(但是在其他数据库中可能不是这样),而且与乐观锁相比,悲观锁有很多优点。不过,它需要一个到数据库的有状态连接,如客户端/服务器连接,因为无法跨连接持有锁。
            正是因为这一点,在当前的许多情况下,使用悲观锁是不现实的。在过去,客户端/服务器应用上可能只有数十个或上百个用户,悲观锁是我们的不二选择。如今,对大多数应用来说,都建议使用乐观锁。
            
            在乐观并发控制的可以方法中,我更倾向使用版本列的方法,增加一个时间戳列。

        阻塞:
            如果一个会话持有某个资源的锁,而另一个会话在请求这个资源,就会出现阻塞。这样一来,请求的会话会被阻塞,他会“挂起”,直至持有锁的会话释放锁定的资源。
            数据库有5个常用的DML语句可能会引起阻塞,它们是:insert、update、delete、merge和select for update。对于一个阻塞的select for update,解决方案很简单:只需要增加nowait子句(阻塞会报错)

            1.阻塞的 insert
                insert 阻塞的最常见的情况:你有一个带主键的表,或者表上有唯一性约束,但有2个会话试图用同样的值插入一行,如果是这样,其中的一个会话就会阻塞,直到另一个会话提交或回滚为止。
                发生insert 阻塞通常是因为应用允许最终用户自己生成主键/唯一列值。为避免这种情况,最容易的做法是使用一个序列或sys_GUID()内置函数来生成主键/唯一列值。序列/sys_GUID()是被设计用于在多
                用户环境中,以高并发的方式生成唯一键值。如果这2个都没办法用,并且必须允许最终用户生成可能重复的键,那你可以使用手工锁来避免这个问题,这里的手工锁通过内置的DBMS_LOCK 包来实现

                使用一个预分配的空间来记录表中主键的值,每个主键一一对应预分配空间值,如果需要insert一个值,则在这个空间使用排他锁来锁定,那么其他会话如果需要insert一个值,则先在这个空间里获取
                锁,如果获取不到则抛出异常,这样就不会出现阻塞。
            
            2.阻塞的update、delete、merge
                这种情况运用上面提到的悲观锁或者乐观锁可以解决。
            
        死锁:
            如果有2个会话,每个会话都持有另一个会话想要的资源,此时就会出现死锁。
            出现死锁的头号原因是外键未加索引(第二号原因是表上的位图索引遭到并发更新),在以下情况下,Oracle在修改父表后会对子表加一个全表锁
                1.如果更新了父表的主键,由于外键上没有索引,索引子表会被锁住
                2.如果删除了父表中的一行,整个子表也会被锁住(由于外键上没有索引)
            
            未加索引的外键还会出现以下情况:
                1.当你使用了 ON DELETE CASCADE ,而且没有对子表加索引。如果从父表中删除多行,父表中没删除一行就要扫描一次子表
                2.当你从父表查询子表。你会发现如果没有索引的话会使查询速度变慢
            
            当满足以下条件时可以不加外键索引
                1.不会从父表删除行
                2.不会去更新父表的唯一键/主键
                3.不会从父表连接到子表
            
        锁升级:
            出现锁升级时,系统会降低锁的粒度。
            Oracle会尽可能的尝试使用低级别的锁,如有必要,再把这个锁转换为一个所限更多的级别。如果用FOR UPDATE 子句从表中选择一行,就会创建2个锁。一个锁放在所选行上。另一个锁是ROW SHARE TABLE锁,
            放在表本身上。
        
    锁的类型:
        在Oracle中主要有3类锁:
            1.DML锁:DML代表数据操纵语言。一般是指select、insert、update、merge和delete语句。DML锁机制允许并发执行数据修改。
            2.DDL锁:DDL代表数据定义语句,如CREATE和ALTER、语句等。DDL锁可以保护对象结构定于
            3.内部锁和闩:Oracle使用这些锁来保护其内部数据结构。例如,Oracle解析一个查询并生成优化的查询计划时,它会把库缓存闩上,将查询计划放在那里,以供其他会话使用。闩是Oracle采用的一种轻量级
            的低级串行化设备,功能上类似于锁。
        
        DML锁:
            DML锁用于确保一次只有一个人能修改某一行,而且这时别人不能删除这个表。
            
            TX锁:
                事务发起第一个修改时会得到TX锁(事务锁)。事务的发起时自动的。TX锁会被一直持有,直至事务执行提交(commit)或回滚(rollback)。Oracle并没有一个传统的锁管理器以及锁管理器为系统
                中锁定的每一行维护的一个长长的列表。Oracle的锁是做为数据的一个属性被保存的,所以锁的开销并不大。

                如果数据库有一个传统的基于内存的锁分离器,在这样的数据库中,对一行锁定的过程一般如下:
                    1.找到想锁定的那一行的地址
                    2.在锁管理器中排队(作为一种常见的内存中的结构,锁管理器必须是串行化的)
                    3.锁定列表
                    4.搜索列表,查看别人是否已经锁定这一行
                    5.在列表中创建一个新的条目,表明你已经锁定了这一行
                    6.对列表解锁
                修改之后:
                    1.再次排队
                    2.锁定列表
                    3.在这个列表中搜索,并释放你的所有锁
                    4.对列表解锁
                
                在Oracle中:
                    1.找到想锁定的那一行地址
                    2.到达那一行
                    3.就地锁住这一行,就是在行的位置上,而非某个大列表。
                
                仅此而已,由于锁是数据的一个属性,所以Oracle不需要传统的锁管理器。事务只是找到数据,如果数据还没被锁定,则对其锁定。
                在Oracle中对数据进行锁定时,会查看对于的行的是否被锁定,就是查看数据行的一个事务ID,每一个行数据都有一个事务ID,由撤销段号、槽和序列号组成。如果此事务ID是在活动的则此行数据被
                锁定,反之则更新事务ID,把自己的事务ID更新到数据行中。
            
            TM锁:
                TM锁用于确保在修改表的内容时,表的结构不会改变。例如,如果你已经更新了一个表中的行,那同时也会得到这个表上的TM锁。如果有其他用户试图修改此表结构,则会抛出错误
            
                尽管每个事务只能得到一个TX锁,但是TM锁则不同,修改了多少个对象,就能得到多少个TM锁。
        DDL锁:
            在DDL操作中会自动为对象加DDL锁,从而保护这些对象不会被其他会话修改。例如,如果我执行一个DDL操作ALTER TABLE T,通常表T上就会加上一个排他DDL锁,以防止其他会话得到这个表的DDL锁和TM锁

            在DDL语句执行期间会一直持有DDL锁,一旦操作执行完毕就立即释放DDL锁。
            每条CREATE、ALTER等语句实际上都如下执行(伪代码)
                BEGIN
                    COMMIT;  -- 会先提交
                    DDL-STATEMENT
                    COMMIT;
                EXCEPTION
                    when others then rollback;
                END;
            
            有3种类型的DDL锁:
                1.排他DDL锁:这会防止其他会话得到它们自己的DDL锁或TM锁。这说明,在DDL操作期间可以查询一个表,但是无法以任何方式修改这个表
                2.共享DDL锁:这些锁会保护所引用的对象的结构,使之不会被其他会话修改,但是允许修改数据
                3.可中断解析锁:这些锁允许一个对象(如共享池中缓存的一个查询计划)向其他对象注册其依赖性。如果在被依赖的对象上执行DDL,Oracle会查看已经注册的依赖对象列表,并使这些依赖对象无效。
                                例如,有一个查询SQL的查询计划已经解析完放到了内存中,但是你又重新编译了,这时可中断解析锁就会把这个缓存里的查询计划无效化
            
    闩:
        闩是轻量级的串行设备,用于协调对共享数据结构、对象和文件的多用户访问
        闩就是一种为保持极短时间而设计的锁(例如,修改一个内存中数据结构所需的时间)。闩用于保护某些内存结构,如数据库块缓存或共享池中的库缓存。
        由于许多请求者可能会同时等待一个闩,你会看到一些进程等待的时间比其他进程要长一些。闩的分配相当随机,这要看运气好坏,闩释放后,紧接着不论哪个会话请求闩都会得到它。等待闩的会话不会排队,
        只是一大堆会话在不断的重试。

        闩自旋:
            闩是一种锁,锁是串行化设备,而串行化设备会妨碍可扩展性。所以尽量减少所需使用闩的数量
            等待闩可能是一个代价很高的操作。如果闩不是立即可以用的,我们就得等待(大多数情况下就是如此)。在一台多CPU机器上,我们的会话就会自旋,也就是说,在循环中反复的尝试得到闩。出现自旋的原因
            是,上下文切换的开销很大(上下文切换时指被“踢出”CPU,然后尝试又必须调度会CPU)。所以,如果我们不能立即得到闩,我们就会继续呆在CPU上,并立即再次尝试,而不是先休眠,放弃CPU,等到必须
            调度回CPU时才再次尝试。
    
    互斥锁:
        是比闩更轻量级的串行设备。功能比闩要少。

 

 

第七章 - 并发和多版本控制

开发基于数据库的多用户应用时,最大的难题之一是:一方面要力争最大的并发访问,另一方面还要确保每个用户能以一致的方式读取和修改数据。

什么是并发控制:
    并发控制是数据库提供的一系列功能,它允许多人同时访问和修改数据。锁是Oracle管理共享数据库资源的并发访问并防止并发数据库事务之间“相互干渉”的核心之一。
    
    Oracle对并发的支持不只是高效的锁定,它还实现了一种多版本控制体系,这种体系结构提供了一种受控但高度并发的数据访问。多版本控制是指,Oracle能同时物化多个版本的数据,这也是Oracle提供数据读一致性的
    基础(读一致性是指相对于某个时间点Oracle会返回一致的结果)
    
    默认情况下,Oracle的读一致性多版本适用于语句级,我们也可以将其调整为事务级。数据库中的事务的基本作用是将数据库从一种一致状态转变为另一种一致的状态。

事务隔离级别:
    ANSI/ISO SQL 标准定义了4种事务的隔离级别,对于相同的事务,采取不同的隔离级别分别有不同的结果。这些级别是根据3种“现象”定义的,以下就是隔离级别可能允许或不允许的3种现象
        1.脏读:你能读取未提交的数据,也就是脏数据。
        2.不可重复读:如果你在T1时间读取某一行,在T2时间重新读取这一行时,这一行可能已经有所修改,也行它已经消失,也可能被更新了,此时等到的结果与之前不一样
        3.幻读:如果你在T1时间执行一个查询,而在T2时间再执行这个查询,,此时可能数据库中新增了一些行而影响你的结果。与不可重复读的区别:在幻读中,已经读取的数据没发生变化,T2比T1有更多的数据

    事务的隔离级别:
        隔离级别            脏读            不可重复读            幻读
        READ UNCOMMITTED    允许            允许                允许
        READ COMMITED        --                允许                允许
        REPEATABLE READ        --                --                    允许
        SERIALIZABLE        --                --                    --
    
    Oracle明确的支持标准中定义的READ COMMITED(读已提交)和SERIALIZABLE(可串行化)隔离级别。在SQL的标准的定义中,READ COMMITED不能提供读一致性,而READ UNCOMMITTED(读未提交)级别
    用来实现非阻塞读。不过在Oracle中,READ COMMITED则有得到读一致查询所需的所有属性。

    READ UNCOMMITTED:
        READ UNCOMMITTED隔离级别允许脏读,Oracle没有使用脏读这样的机制,它甚至不允许脏读。脏读不是一个特性,而是一个缺点。Oracle根本不需要脏读。
    
    READ COMMITED:
        READ COMMITED隔离级别是指事务只能读取数据块中已经提交的数据。READ COMMITED可能是最常用的隔离级别了,这也是Oracle数据块的默认模式,很少看到使用其他的隔离级别
        存在脏读的现象:
            如果在一条语句中查询了多行,除了Oracle外(使用了多版本和读一致查询),在几乎所有其他的数据库中,READ COMMITED隔离都可能“退化”得像脏读一样。
            一个会话在求和一个表中的某个字段时,另外的一个会话对这个表进行修改,如果第一个会话已经读取了前10行,第二个会话把第一行的数据-100并把它加到第100行中,则第一个会话中的求和出来
            的值是错误的,并且还被第二个会话阻塞、
    
    REPEATABLE READ:
        REPEATABLE READ的目标是提供一个这样的隔离级别:他不仅能给出一致的正确答案,还能避免丢失更新。
        1.得到一致的答案:
            如果隔离级别是REPEATABLE READ,那么同一个查询相对于某个时间点返回的结果是一致的。大多数数据(不包括Oracle)都通过使用低级的共享读锁来实现可重复读,共享读锁会防止其他会话修改我们
            已经读取的数据,但是这种机制会降低并发性(甚至会造成死锁)。Oracle则采用更具并发性的多版本模型来提供一致性读。
            
        2.丢失更新:另一个可移植性问题
            在采用共享读锁的数据库中,REPEATABLE READ的一个常见用途是防止丢失更新。
            当你把应用移植到一个没有使用共享读锁作为底层并发控制机制的数据时,就会发现实际情况和你预想的不一样
    
    SERIALIZABLE:
        一般认为这是最搜限的隔离级别,但是它也提供了最高程度的隔离性。SERIALIZABLE事务在执行时,数据库提供给它的环境就好像是一个当用户环境:貌似没有任何别的用户在修改数据库中的数据。
        Oracle实现SERIALIZABLE事务的方式很简单:将原本在语句级的读一致性扩展到事务级

        Oracle采用一种乐观的方法来实现串行化,它认为你的事务想要更新的数据不会被其他事务所更新,而且把宝压在这上面,一般情况下(尤其是短小事务的OLTP系统中)确实是这样的,所以说通常这个宝
        是押对的。在其他数据库系统中这个隔离级别通常会降低并发性,但是在Oracle中,倘若你的事务在执行期间没有别人更新你的数据,那么我们就能提供与非SERIALIZABLE事务同等程度的并发性。另一方面,
        这也是有缺点的,如果宝押错了,你就会得到一个ORA-08177错误。
        所以你希望高效的使用SERIALIZABLE隔离级别,请确认你的系统符号以下3个条件:
            1.一般没有其他人修改相同的数据
            2.需要事务的一致性
            3.事务都很短(这有助于保证第一点)
    
    READ ONLY:
        READ ONLY事务与SERIALIZABLE很相似,唯一的区别是READ ONLY事务不允许修改,因此不会遇到ORA-08177错误。READ ONLY事务的目的是相对于某个时间点,报告的内容应该是一致的。
        
        Oracle的READ ONLY隔离级别的实现方式与单条语句类型,他也使用了同样的多版本控制机制。Oracle会根据需要从undo段重新创建数据,并提供版本开始数据的原样。如果有人修改了你将要修改的数据,
        就会可能发生这种情况。所有数据的修改都会记录在undo段中,但是undo段以一种循环的方式使用,这与重做日志非常相似。报告允许的时间越长,重建数据所需的undo信息就越有可能被覆盖掉。当你需要
        的那部分undo段已经被另外某个事务占用时,就会得到ORA-01555错误,我们的事务只能从头再来。唯一的解决方案就是为系统适当的确定undo段的大小。
    
多版本读一致的含义:
    多版本控制是一个好东西,它不仅能提供一致(正确)的答案,还有高度的并发性。

写一致性:
    到目前为止,我们已经了解到读一致性:Oracle可以使用undo信息来提供非阻塞的查询和一致(正确)的读。但是出现下面这种情况,Oracle会如何处理呢:
    在一个会话允许这个语句,update t set x = 2 where y = 5 但是在第二个会话在第一个会话途中修改了y的值变为y = 6。
    Oracle会这样处理:
        在第一个会话开始时,y = 5,但是在进行修改时却发现y的值变为6了,y的值更新了,Oracle因为不能修改块的老版本,这样会造成不一致性(违反数据库中的事务的基本作用是将数据库从一种一致状态转变
        为另一种一致的状态),所以会出现开始写修改(重启动)。

    隔离级别                实现                写阻塞读                读阻塞写                读易发生死锁                不正确的查询结果                丢失更新                锁升级或限制
    READ UNCOMMITTED        非Oracle            否                        否                        否                            是                    是                        是
    READ COMMITED           非Oracle            是                        否                        否                            是                    是                        是
    READ COMMITED           Oracle              否                        否                        否                            否                    否                        否
    REPEATABLE READ         非Oracle            是                        是                        是                            否                    否                        是
    SERIALIZABLE            非Oracle            是                        是                        是                            否                    否                        否
    SERIALIZABLE            Oracle              否                        否                        否                            否                    否                        否

 

标签:文件,艺术,数据库,编程,进程,内存,Oracle,服务器
From: https://www.cnblogs.com/xcxy-boke/p/16757682.html

相关文章

  • Oracle 中Hint用法
    一、Hint是Oracle提供的一种SQL语法,它允许用户在SQL语句中插入相关的语法,从而影响SQL的执行方式。 二、在使用Hint的时候需要注意一点的是,并非在任何时候Hint都起作用,原......
  • 纯编程式驱动IOC
    编程式创建beanpublicstaticvoidmain(String[]args)throwsException{AnnotationConfigApplicationContextctx=newAnnotationConfigApplicationContext();......
  • C++/Python混合编程
    以C++为底层基础,Python作为上层建筑,共同搭建起高性能、易维护、可扩展的混合系统。Python本身就有C接口,可以用C语言编写扩展模块,把一些低效耗时的功能改用C实现......
  • 编程的学习总结
    一,实验代码   #include<stdio.h>intmain(){printf("Hello,world!")}  二,设计思路 第一步:打框架第二步:算法是排序   第三步:编译,查......
  • 对比python学julia(第四章:人工智能)--(第一节)OpenCV编程初步(3)
    1.4. 人脸检测(续上)3.检测视频中的人脸在VSCode环境中,新建一个空白源文件,以detect_video.jl作为文件名保存到项目文件夹中,然后编写程序检测视频流......
  • Linux多线程服务端编程 pdf
    高清文字版下载链接:https://pan.baidu.com/s/1Ar0sbiycp70BdNysXfkg2w点击这里获取提取码 ......
  • Oracle11g完全卸载的详细步骤(超管用)
    由于需要,这会儿需要卸载掉本机上的oracle11g数据库(我是在Windows7系统上装的),在网上搜的了挺多方法的,有些说的不清楚。下面小编给大家整理关于oracle11g卸载步骤,一起看看......
  • Oracle的EXP、IMP命令导入导出数据
    EXP:①全库备份expsystem/123456@prodfull=yfile=~/backup/database.dmplog=~/backup/database.logbuffer=80960000—不包括sys用户即数据字典没有导出如想导出sys......
  • (函数)编程:定义一个函数fun(a,b,c),返回一元二次方程ax^2+bx+c=0的两个解。程序运行后输
    样例输入231 样例输出(-0.5,-1.0) 样例输入246 样例输出输入错误,无解 解题代码importmathdeffun(a,b,c):dt=b*b-4*a*cx1=(-b......
  • LCCUP 22秋季编程大赛
    ​​LCCUP22秋季编程大赛​​总结:交的时候,460名然后慢慢得掉到最后580。第三题一开始没读好题,浪费了很长时间。四五题没有什么思路,直接就没写了。(横向对比,比春季进步了......