Oracle高性能自动化运维
上QQ阅读APP看书,第一时间看更新

第2章

Oracle内存体系结构

通过第1章的学习,我们知道Oracle数据库主要由内存结构和物理结构组成。Oracle实例(Instance)是Oracle内存结构的载体,由若干大小不一、功能独特的池(Pool),以及内存数据保护机制(Latch/Lock)组成。

本章着重对Buffer Cache/Library Cache内存结构以及Latch/Lock工作原理进行讲解,帮助读者掌握Oracle内存结构优化设计、性能诊断等技巧。

2.1 闩(Latch)

2.1.1 Latch简介

对于对称多处理机(SMP)系统而言,在同一时间点,所有CPU都可以请求修改同一内存。在理想状态下,“请求”“更改”操作是由单个CPU独立完成的,而不会被拆分成多个时间片处理。在特殊情况下,“请求”“更改”操作将被拆分成多个时间片处理,这时操作系统就会使用上下文结构来保护内存数据的完整性。

Oracle闩(Latch)的作用与上下文类似,主要是用于保护Oracle内存结构被独占访问、修改,以防止非法的内存数据篡改,保证内存数据的完整性。常见的Latch有:

❑ Library Cache Latch:保护Library Cache内存结构;

❑ Shared Pool Latch:保护Shared Pool内存结构;

❑ Cache Buffers Chain Latch:保护Cache Buffers Chain内存结构;

❑ Cache Buffers LRU Chain Latch:保护Cache Buffers LRU Chain内存结构。

2.1.2 Latch Level

当CPU执行作业的时候,操作系统可以通过中断、异常等方式来“打断”CPU当前正在执行的任务,从而将CPU资源释放出来供更优先的进程使用。

优先级的概念大家并不陌生,在日常生活中也随处可见。例如,我们可以将车道当作CPU,普通车辆当作Process A,消防车辆当作Process B。当消防车辆(Process B)需要占用车道(CPU)时,由于消防车辆(Process B)使用车道(CPU)的优先级高于普通车辆(Process A),那么消防车辆(Process B)将优先占用车道资源(CPU),即使普通车辆(Process A)已经占用了车道(CPU),也会让出车道资源(CPU)优先给消防车辆(Process B)使用。可以看出,优先级主要是用于调度资源使用先后顺序的。

Oracle Latch Level功能与优先级类似,主要用于Latch资源的调度。Latch Level的特点如下:

❑ Latch Level被预定义在Oracle系统中;

❑ Latch Level范围在0~16之间(11g);

❑ Latch Level用于调度保护内存结构,缓解修改内存结构时产生的Latch争用。

可以通过查询v$latch获取Latch Level相关信息,如下所示:

2.1.3 Latch获取模式

在介绍Latch获取前,先了解几个与Latch获取相关的概念。

❑ 自旋(Spin):Spin表示“自旋”,即当Latch被某个Process独占时,另外一个Process也想获取该Latch,由于独占的特性使得另一个Process处于Spin状态:自旋等待。Latch Spin阈值由参数spin_count决定,如下所示:

❑ 休眠(Sleep):当Process Spin等待达到spin_count阈值时,Process就会放弃获取Latch资源,进入短暂的休眠状态。当Latch资源被释放后,系统将唤醒(Wake-Up)该Process继续获取该Latch。

❑ 上下文切换(Context Switch):对于多任务处理系统而言,CPU需要处理所有的程序操作。当用户进程来回切换程序时,需要记录这些进程执行的状态信息。上下文切换允许记录、恢复处于正在运行状态的程序,保证程序间能够顺利完成上下文切换操作。

在上下文切换过程中,CPU会停止处理当前正在处理的程序,并保存当前程序的运行状态信息以便后续断点运行。从这个角度来看,上下文切换有点类似书的页码,在来回切换书页的同时,需要记住当前书页的页码以便快速切换。在操作系统中,上下文切换过程中的“页码”信息保存在进程控制块(PCB)中。

上下文切换主要发生在以下几种场景:

❑ 中断处理;

❑ 多任务处理;

❑ 用户状态切换。

在中断处理中,其他程序“打断”了当前正在运行的程序,当CPU接收到中断请求时,会在正在运行的程序和发起中断请求的程序之间进行一次上下文切换;在多任务处理中,CPU会在不同程序之间来回切换,每个程序都有相应的处理时间片,CPU在两个时间片的间隔中进行上下文切换;对于一些特殊的操作系统,当进行用户状态切换时也会进行一次上下文切换。

Latch获取的相关概念就介绍到这里,接下来我们继续讨论Latch获取的两种模式。

1. No-Wait模式

No-Wait模式适用场景:当Process请求获取Latch时,Latch资源池中有空闲的Latch资源。在这种模式下,获取Latch的主要步骤如下:

1)Oracle Process尝试获取一个Latch,这个过程不需要Spin和Sleep;

2)更新CPU上下文内容,实现上下文切换;

3)当请求获取的Latch没有被其他Process占有(Free)时,那么就可以立即获取到该Latch;

4)当获取的Latch已经被其他进程抢先独占了(Busy),那么就搜索下一个空闲的Latch,直到找到符合步骤3)的Latch。

2. Willing-To-Wait模式

Willing-To-Wait模式适用场景:当Process请求获取Latch时,Latch资源池中没有空闲的Latch资源。在这种情况下,需要使用Willing-To-Wait模式获取Latch资源。Willing-To-Wait模式分为两种获取类别:

❑ Immediate Get:一次快速获取,不需要Spin;

❑ Spin Get:需要Spin等待。

○ 当Latch获取失败后,将会不断地重试获取:Spin或者Sleep等待;

○ 在等待Latch释放的过程中,处于Spin或者Sleep状态;

○ 在未达到spin_count阈值前,等待时间累积增加;

○ 当达到spin_coun阈值时,如果仍然没获取到Latch,则会记录Latch Miss信息。在这种情况下,需要人为干预进行优化调节。

2.1.4 Latch获取等待

1. Latch获取等待原理

Latch获取等待原理如图2-1所示。

图2-1 Latch获取等待

通过图2-1不难看出:

❑ Process A独占该Latch;

❑ Process B想要获取该Latch,那么只能等待;

❑ Process B在等待过程处于Spin或者Sleep状态;

❑ Process B等待获取该Latch时等待超时,则继续等待或者放弃获取该Latch。

2. Latch获取等待命中率

当Latch获取等待超过阈值后,就会将Latch Miss记录到系统统计信息中,因此需要关注Latch获取等待的命中率,如下所示:

可以看出,ALL LATCHES的获取命中率为83.57%,相对比较低。一般而言,如果Spin获取命中率超过90%,那么Latch获取就是比较优化的。因此对于上述示例,需要进行Spin获取优化。

与此同时,考虑到Latch获取性能,我们还需要关注Latch获取等待休眠率,如下所示:

可以看出,Latch的休眠率比较低,因此不需要对Latch Sleep进行优化。

2.1.5 Latch资源清理回收

当Latch资源被释放或者在Latch持有时系统发生异常,Oracle将会对Latch资源进行清理回收:

❑ 正常情况下,Latch调用自己特有的清理方法完成Latch资源清理回收;

❑ 在异常的情况下,如果Process在获取Latch过程中由于某些原因“死掉”,那么受到该Latch保护的内存结构可能会出现异常,这时PMON进程会介入,进行清理操作;

❑ PMON清理操作将恢复异常状态的内存结构;

❑ 当清理结束后,PMON释放Latch资源供其他Process获取。

我们可以通过查询x$kgllc(10g)获取Latch清理信息,如下所示:

2.2 队列锁(Enqueue Lock)

2.2.1 Lock与Latch的区别

Oracle锁(Lock)也被称为队列锁(Enqueue Lock),而Latch则被称作为“细粒度”的锁。Lock与Latch的区别如表2-1所示。

表2-1 Lock与Latch区别

不难看出:

❑ Oracle使用Latch保护内存数据被间歇地、短持续地访问;

❑ Latch不适合保护持续时间相对较长的资源(Resource),在这种情况下,就需要使用Lock持续锁定;

❑ Lock允许并发等待:当前不可获取的Resource会被放入请求队列中进行排队(FIFO),这样就避免了类似Latch的Spin等待;

❑ Lock允许多个Session访问同一个Resource(Session互相兼容),但是Latch必须是独占地排他访问。

2.2.2 常见的Lock

Oracle常见的Lock有四种,下面将逐一介绍。

1.事务锁和行级锁

行级锁(Row-Level Lock)是Oracle引以为傲的锁定模式。当事务(Transaction)更改了一行,那么该事务的唯一标志(ITL)就会被记录在数据库块头(Data Block Header)中。

在Transaction开始前,系统从回滚段头的事务表(Undo Transaction Table List)中分配一个ITL事务槽,同时将ITL记录到被事务影响的Data Block中。与此同时,被该事务影响更改的行将会使用Lock Bytes索引指向该ITL。

提示

有关ITL以及事物的内容可参考第3章、第4章的相关内容。

这样,当新的事务需要更改同一行时,将会发现有尚未提交的事务(Uncommited Tran-saction)正在修改该行,那么该事务就会被阻塞等待(Transaction Block Wait)。

一旦事务提交或者回滚,事务锁就会被释放。即那个被阻塞等待的事务就锁定该行了。

注意

Rollback To Savepoint不能释放先前的行级锁,因此会阻塞后续对该行级锁的获取。只有回滚到事务开始的最初状态或者事务提交后才能获取该行级锁。

2. Buffer Lock

行级锁是保护数据中粒度较小的锁,Oracle提供了Buffer Block级别的锁(Buffer Lock)来保护修改Buffer Cache中的Buffer Block。Buffer Lock仅提供了Read/Write锁定模式,用于保护Buffer Block的完整性。

3. Data Dictionary Lock

当数据库字典对象(Procedure、View等)被调用的时候,必须保证数据字典对象的完整性、有效性。数据字典锁(Data Dictionary Lock)就是用于保护数据字典对象完整性的,通过Row Cache Enqueue Locks进行保护。

4. PCM实例锁

PCM实例锁(Parallel Cache Management Instance Lock)主要用于集群环境中,保护分布式集群资源的协调分配。

2.2.3 Lock相关参数

Oracle队列锁是通过Enqueue Hash Chain结构实现的。当需要访问Enqueue Hash Chain时,必须在“enqueue hash chains latches”保护下才能进行。

“enqueue hash chains latches”数量由参数_enquenue_hash_chain_latches决定,默认值为CPU数量。

1)查看CPU数量:

2)查看参数_enquenue_hash_chain_latches

可以看到,“enqueue hash chains latches”数量与CPU数量是一致的。

2.2.4 Lock先请求先服务机制

在多并发Session的场景中,需要先获取Resource的X锁定才能对该Resource进行修改。与此同时,当其他Session也同样请求修改该Resource时,则必须按照先请求先服务的方式进入请求队列进行排队等候。可以通过下面示例验证。

可以看出:Enqueue Lock的获取遵循先请求先服务的原则。因此,在高并发业务环境中,业务架构设计必须遵循这个原则,以保证数据库的高并发运行。

2.3 Library Cache

2.3.1 Library Cache与SQL游标

Library Cache主要用于存放SQL游标,而SQL游标最大化共享是Library Cache优化的重要途径,可以使SQL运行开销最低、性能最优。

1. SQL语句与父游标及子游标

在PL/SQL中,游标(Cursor)是数据集遍历的内存集合。而从广义上讲,游标是SQL语句在Library Cache中的内存载体。SQL语句与游标关系如下:

1)一条SQL语句包含一个父游标(Parent Cursor)和一至多个子游标(Child Cursor),如图2-2所示。

图2-2 SQL语句与游标

2)SQL语句通过SQL_ID唯一标识父游标,如下所示:

从上述示例可以看出,SQL语句使用SQL_ID唯一标识父游标(V$SQLAREA),同时该SQL语句仅包含一个父游标和一个子游标。

3)不同的SQL语句的父游标也不同,如下所示:

可以看出,两个不同SQL语句对应的SQL_ID也不相同,产生了不同的父游标。

提示

SQL语句父游标不相同,其对应的子游标也肯定不同。关于游标内存结构介绍,可以参考5.1节。

2.父游标

(1)父游标特点

父游标的主要特点如下:

❑ 父游标是由SQL语句决定的;

❑ 父游标使用SQL语句的SQL_ID唯一标识;

❑ 父游标包含一到多个子游标;

❑ 父游标与参数cursor_sharing紧密相关。

(2)父游标组成结构

父游标的主要组成结构如表2-2所示。

表2-2 父游标组成结构

父游标组成结构单元之间的关系,如图2-3所示。

图2-3 父游标组成结构

(3)父游标相关查询

父游标信息可以通过V$SQLAREA视图进行查询。V$SQLAREA的主要特点有:

❑ V$SQLAREA中一条记录表示一个父游标,如下所示:

可以看出在V$SQLAREA视图中,SQL_ID是唯一的,从侧面也可以证明V$SQL-AREA中一条记录代表一个父游标。

❑ V$SQLAREA只包含父游标的相关信息。

(4)父游标相关参数

参数cursor_sharing决定父游标被共享的模式,用于减少解析带来的开销,提升SQL执行效率。cursor_sharing的3种模式如下:

❑ EXACT(默认模式),如下所示:

❑ FORCE;

❑ SIMILAR。

接下来对这3种模式进行详细介绍。

1)cursor_sharing= EXACT:默认模式。只有SQL语句内容完全一样,才会共享父游标(SQL语句之间才会共享)。也就是说,当用户端发起的SQL语句只要有一点不相同,就会产生不同的父游标,从而不会共享SQL父游标。如下所示:

2)cursor_sharing=FORCE:当模式设置为FORCE时,将会强制优化器共享父游标,而不管执行计划是否最优。当条件允许时,可以采用这种方式来减少解析开销。如下所示:

可以看出,在FORCE模式下,两条内容不同的SQL强制共享了父游标(使用系统绑定变量)。

提示

FORCE模式建议不要过度使用,虽然这种模式会强制SQL共享父游标,但是这样可能会忽略CBO优化器最优的执行计划,使得SQL执行不是最优化的。

3)cursor_sharing=SIMILAR:模式SIMILAR表示优化器在一定条件下会自动选择共享游标:

❑ SQL语句几乎完全相同;

❑ 执行计划相同或者执行计划更优;

❑ 忽略SQL语句文字内容差异。

可以通过以下示例进行验证。

示例1:参数变化导致游标共享差异。

可以看出,当模式设置为SIMILAR时,只要SQL语句相似就可以共享游标。

示例2:父子游标。

示例2可以概括为如图2-4所示的关系。

图2-4 父子游标与cursor_sharing

通过图2-4可以看到,一个父游标可以包含多个子游标,从而验证了图2-2的正确性。

3.子游标

(1)子游标特点

子游标的主要特点有:

❑ V$SQL中一条记录对应一个子游标;

❑ 子游标与绑定变量(Bind Variable)、NLS参设置等相关;

❑ 子游标与参数optimizer_mode紧密相关。

(2)子游标组成结构

子游标的主要组成结构如表2-3所示。

表2-3 子游标组成结构

子游标组成结构单元之间的关系,如图2-5所示。

图2-5 子游标组成结构

提示

有关父子游标的详细信息,可以参考5.1节。

(3)子体游标相关查询

子游标信息可以通以V$SQL(X$KGLCURSOR_CHILD)视图进行查询。V$SQL的主要特点有:

❑ V$SQL中一条记录代表一个子游标。如下所示:

可以看到,一个SQL_ID(父游标)包含了多条记录,每条记录代表一个子游标。

❑ V$SQL包含了父游标和子游标信息。

(4)子游标相关参数

参数optimizer_mode用于设置子游标的CBO优化器模式。可以通过查询V$SQL_SHARED_CURSOR. OPTIMIZER_MISMATCH验证子游标不匹配(missmatch)的原因:是否由参数optimizer_mode导致。如下所示:

可以将上面内容概括为如图2-6所示的关系。

图2-6 父子游标与optimizer_mode

提示

参数optimizer_mode相关设置可参考6.1.1节。

2.3.2 Library Cache内存结构

1. Library Cache内存结构概览

Oracle使用内存管理器(KGH)对Library Cache内存进行管理。Library Cache包含了一张由Hash Bucket组成的Hash Table,每个Hash Bucket以链表的方式与Object Handle进行链接,如图2-7所示,ObjectHandle主要负责Library Cache Lock(Pin)控制;同时,Object Handle指向LCO(Library Cache Object), LCO由各种类型的对象组成,如图2-8所示。

图2-7 Hash Table与Object Handle

图2-8 Object Handle与LCO

2. Library Cache内存相关参数

参数_kgl_buckets_count决定了(Library Cache)Hash Bucket的数量,可以通过调节该参数来对(Library Cache)Hash Table进行调节。Hash Table的主要特点:

❑ Hash Table会自动扩展:当所需的Bucket的数量超过当前Hash Table中可容纳的空间时进行扩展;

❑ Hash Table自动扩展的原则为:上一次分配空间的两倍左右;

❑ Hash Table扩展将会分配新的Hash Table来替换掉当前的Hash Table,同时将原Hash Table中的Hash Bucket重新“哈希”到新的Hash Table,最后释放原Hash Table内存;

❑ 参数_kgl_buckets_count与Hash Bucket关系如下所示:

    Hash Buckets=(2^_kgl_bucket_count * 256)

提示

Hash Bucket的数量为2的_kgl_bucket_count次方与256的乘积。

我们可以通过以下示例进行验证。

1)查看参数_kgl_buckets_count的配置,如下所示:

2)查看Library Cache的内存分配情况,如下所示:

可以看到:

❑ LIBRARY CACHE HASH TABLE中的size(131072)为Hash Bucket的数量,count(6394)为LCO Handle的数量;

❑ 131072=29× 256,与公式完全符合;

❑ 131072=124829(Chain_Size:0)+6092(Chain_Size:1)+151(Chain_Size:2)。

提示

Hash Bucket是由Hash_Chain_Size不同的Hash_Chain组成的,这样的好处是可以提高Library Cache空间使用率,减少内存碎片的产生。

3. Library Cache Dump

Library Cache Dump内容如下所示:

可以看出,Library Cache是由Hash Buckte、LCO Handle以及LCO等内存结构组成的。

2.3.3 Library Cache Lock(Pin)

1. Library Cache Lock(Pin)概述

(1)Library Cache Lock特点

Library Cache包含了SQL解析树、执行计划以及被引用的对象等信息。同时,Library Cache还包含了同义词转换、依赖关系信息以及LCO/LCO Handle等结构。这些信息结构在PL/SQL编译、SQL语句解析执行等场景使用Library Cache Lock进行保护。Library Cache Lock主要特点有:

❑ Library Cache Lock被称为易破坏的解析锁(Parse Lock),负责对SQL/PLSQL解析树(LCO Handle)以及依赖对象进行保护;

❑ Library Cache Lock在SQL/PLSQL解析到内存时为共享(Share)模式,然后以Null模式存在;

❑ 如果SQL/PLSQL的相关(引用)对象定义被更改而导致解析树发生变化,Oracle就会破坏Library Cache Lock的锁定,对引用的LCO进行无效化(Invalidate)处理。在下次调用SQL/PSQL时,需要重新解析、缓存,从而降低了数据库的性能;

❑ Library Cache Lock只有在解析树引用的LCO对象还没有被Pin入内存时才能被破坏。当这些被解析树引用的LCO对象被Pin住后,Library Cache Lock将升级为Excluseive模式,这时就不能对Library Cache Lock进行破坏操作,保护了Library Cache结构的完整性。

(2)Library Cache Pin特点

Library Cache Pin发生在Library Cache Lock之后,在SQL/PLSQL执行操作时产生Pin。Library Cache Pin的主要特点如下:

❑ Library Cache Pin负责对Heap0对象(LCO)以及依赖对象进行保护;

❑ Library Cache Pin是SQL/PLSQL执行调用时产生Pin;

❑ LCO被Pin,其引用的数据库对象都将被Pin;

❑ Library Cache解析树引用的数据库对象被Pin时,其对应的数据字典锁(Row Cache Enqueue Lock)就会保护这些数据库对象,防止DDL破坏;

❑ Library Cache Pin一般是Share模式,当需要修改LCO时,就需要转换成Exclusive独占模式。

(3)Library Cache Lock(Pin)用途

Library Cache Lock(Pin)的主要用途如下:

❑ Library Cache Lock管理并发控制;

❑ Library Cache Pin保证内存数据完整性。

重点

Library Cache Lock在解析、调用等场景被获取,Library Cache Pin在编译、执行等场景被获取,它们的主要用途是保护解析树以及内存数据对象的完整性。

2. Library Cache Lock(Pin)持续性

Library Cache Lock(Pin)持续性分为以下几种级别:

❑ 会话级(Session):Library Cache Lock(Pin)持续到会话结束;

❑ 事务级(Transaction):Library Cache Lock(Pin)持续到事务Commit/Rollback时释放;

❑ 调用级(Call):Library Cache Lock(Pin)持续到Call结果返回时释放。

3. Library Cache Lock(Pin)模式

(1)Library Cache Lock模式

Library Cache Lock有3种模式:

❑ 共享模式(S):读取对象;

❑ 排他模式(X):修改对象;

❑ 空模式(Null):释放状态。

提示

持久存储的对象可以处于上述3种锁定模式,临时对象(Cursor)只能处于Null锁定模式。

(2)Library Cache Pin模式

Library Cache Pin只有2种模式:

❑ 共享模式(S):读取LCO对象;

❑ 排他模式(X):更改LCO对象。

提示

与Library Cache Lock锁定模式不同,持久存储对象和临时对象都可以用S、X模式Pin在内存中。

4. Library Cache Lock(Pin)争用

当Library Cache Lock(Pin)处于排他模式(X)时,若其他Session需要对该Library Cache Lock(Pin)锁定的对象进行修改,就会产生Library Cache Lock(Pin)争用,对数据库库系统性能产生负面影响。

Library Cache Lock(Pin)争用的主要原因如下:

❑ 过量的解析:

○ SQL没有共享;

○ 不必要的软解析;

○ 没有合理使用绑定变量。

❑ 共享SQL被释放出Library Cache。

❑ 没有Pin住频繁使用的SQL/PLSQL对象。

❑ 参数设置不合理:参数session_cached_cursors的值设置过高。Library Cache可能保留大量无效且不被解析的Cursor,将有用的、共享的SQL“刷”出了Library Cache,导致SQL解析争用频繁。

5. Library Cache Lock(Pin)优化

Library Cache Lock(Pin)争用优化最重要的思路就是增加SQL共享,主要考虑以下几个方面:

❑ 合理使用绑定变量,增加SQL共享,减少解析;

❑ 考虑使用DBMS_SHARED_POOL Pin住频繁使用的SQL/PLSQL对象;

❑ 减少对PLSQL/VIEW/PACKAGE等对象的编译/重编译操作;

❑ 减少PLSQL/VIEW/PACKAGE等对象的无效化(Invalidate)操作;

❑ 合理使用参数cursor_sharing/optimizer_mode,增加SQL共享;

❑ 合理设置参数session_cached_cursors,优化SQL解析(参考5.1节)。

2.4 Buffer Cache

2.4.1 Buffer(Cache)Pool

1. Buffer(Cache)Pool组成结构

Oracle Buffer Cache由3个Buffer Pool组成,如图2-9所示。

图2-9 Buffer(Cache)Pool组成结构

其中:

❑ Default Pool:默认池,用于缓存常规数据;

❑ Keep Pool:保留池,主要用于缓存频繁更新的小表;

❑ Recycle Pool:回收池,用于缓存随机使用的大表。

2. Buffer Pool与Working Set

Buffer Pool与Working Set的关系如图2-10所示。

图2-10 Buffer Pool与Working Set

可以看到:

❑ Default Pool包含了多个Working Set;

❑ Keep Pool包含了多个Working Set;

❑ Recycle Pool包含了多个Working Set。

我们可以通过查询x$kcbwbpd(Buffer Pool)与x$kcbwds(Working Set)来获取Buffer Pool中Working Set的分布情况,如下所示:

上面查询验证了图2-10的内容:

❑ Default Pool包含12个Working Set;

❑ Keep Pool包含2个Working Set;

❑ Recycle Pool包含2个Working Set。

3. DBWR与Working Set

(1)Working Set组成结构

Working Set由功能独特的List组成,如表2-4所示。

表2-4 Working Set组成结构

(2)DBWR与Working Set的关系

DBWR与Working Set的关系如图2-11所示。

图2-11 DBWR与Working Set

可以看到:

❑ 一个DBWR进程负责多个Working Set;

❑ DBWR进程与特定的Working Set相对应,以轮询(类似CPU时间片分片)的方式进行写数据操作。

(3)DBWR相关参数

参数db_writer_processes/ dbwr_io_slaves决定了DBWR进程启动数量以及DBWR子进程的数量。在特定的场景下,可以通过适当增加这些参数值来达到数据库优化的目的,如下所示:

(4)DBWR写触发条件

DBWR写进程触发条件主要有:

❑ 写“脏”块;

❑ 回收LRU List空间;

❑ 检点(Checkpoint);

❑ RAC Ping Writes。

(5)DBWR写优先级

DBWR进程写磁盘的优先级如表2-5所示。

表2-5 DBWR写优先级

提示

DBWR写优先级是系统内部预定义的,按照内部优先级顺序排序固定。

2.4.2 Cache Buffer Chain(Latch)

1. Cache Buffer Chain(Latch)结构

Cache Buffer Chain(Latch)结构如图2-12所示。

图2-12 Cache Buffer Chain(Latch)结构

接下来对Cache Buffer Chain结构进行验证。

1)查询并获取Cache Buffer Chain(Latch)相关参数,如下所示:

2)Dump Buffers获取Cache Buffer Chains信息:

3)结合前面内容,可以获取如表2-6所示的信息。

表2-6 Cache Buffer Chain(Latch)参数数量关系

不难看出:

❑ Buffer Cache中的Buckets Hash Table包含了大量的Hash Bucket(32768);

❑ Hash Bucket的数量(32768)与Cache Buffer Chain的数量(32768)一一对应,与参数_db_block_hash_buckets(32768)一致;

❑ Cache Buffer Chain Latche的数量(1024)与参数_db_block_hash_latches保持一致;

❑ 一个Cache Buffer Chain Latch负责管理多个Cache Buffer Chain。

提示

Cache Buffer Chain可能存在空(Null)Chain。在这种情况下,Chain上没有挂载BH(Buffer Header)。

2. Cache Buffer Chain(Latch)相关查询

可以通过v$latch/v$latch_children来查询Cache Buffer Chain(Latch)相关信息,如下所示:

可以看到有1024个Cache Buffer Chain(Latch),与初始化参数_db_block_hash_latches完全吻合。

2.4.3 Cache Buffer Pin

一般而言,Cache Buffer Chain(Latch)与Cache Buffer Pin的主要用途有:

❑ Cache Buffer Chain(Latch):用于保护Chains完整性;

❑ Cache Buffer Pin:保护Buffer Block内容(Content)的完整性。

1. Cache Buffer Pin与(User)Wait List

Cache Buffer Pin结构中包含了Pin相关的信息,当被阻塞的会话没有请求到Pin时,就可以将自己地址信息等提交到Wait list的末端进行等待。等待事件为“buffer busy wait”,由参数_buffer_busy_wait_timeout决定,默认为1秒钟。如下所示:

❑ 当Session以Exclusive(X)模式Pin住Buffer Header后,请求Pin住该Buffer Header的Session都会加入到Wait List的末端,同时触发“buffer busy wait”等待;

❑ 当等待时间超过_buffer_busy_wait_timeout阈值后,等待Session将被唤醒,同时Pin持有者和等待者之间将会产生死锁。解决死锁的主要方式是释放该Buffer Header上的所有Pin,同时再次尝试获取该Buffer Header Pin。

2. Cache Buffer Pin工作模式

当获取了Cache Buffer Chain(Latch)后,可以进行以下操作:

❑ 从该Chain中添加或者删除Buffer Header;

❑ 从Pin List中添加或者删除Pin;

❑ 更改Pin的模式(S/X)。

注意

只有在X模式下的Pin才能修改Buffer内容。一般来说,当将Pin加在Buffer上后,没有必要立即就释放该Pin。在某些情况下,查询优化器“预测”在不久的将来需要再次访问该Buffer,那么就会将Pin延迟到下一次User Call。从某种意义上讲,Oracle Pin住Buffer直到下一次User Call,是访问Buffer成本最低的方式。

3. Cache Buffer Pin与参数_db_block_max_cr_dba

当需要更改Buffer Block的内容时,必须使用Cache Buffer Pin进行保护。如果这时对该Buffer Block进行查询,那么系统就会以当前Pin住的Buffer Block为基础块,重构CR镜像。

参数_db_block_max_cr_dba限定了单个Block Buffer的最大CR镜像数量,同时从侧面限制了Chain的长度,提高了Chain扫描的速度,默认为6。如下所示:

以下步骤演示了Cache Buffer Chain(Pin)与参数_db_block_max_cr_dba的关系。

1)Dump特定Buffer Block,语法如下:

        alter session set events 'immediate trace name set_tsn_p1 level ts#';
        alter session set events 'immediate trace name buffers level level#'

其中:

❑ ts# = relative_fille# + 1

❑ level# = 4194304× relative_fille# + block#

2)创建基表,并获取表相关信息,如下所示:

在这里:

❑ relative_fille# = 4

❑ ts# = relative_fille# + 1 = 5

❑ level# = 4194304× relative_fille# + block# = 16777551

3)Dump特定Buffer(Level2):

使用命令:

    alter session set events 'immediate trace name set_tsn_p1 level 5';
    alter session set events 'immediate trace name buffer level 16777551'

进行Dump, Dump结果如下所示:

4)整理Dump结果,如表2-7所示。

表2-7 Dump特定Buffers

5)Buffer Header的Hash链表关系如图2-13所示。

图2-13 BH与Chain

从上面的关系中不难看出:

❑ BH间以哈希键值对组成双向链表,从逻辑上组成Cache Buffer Chain;

❑ 单个DBA最大CR为6,与参数_db_block_max_cr_dba完全吻合;

❑ 为了提高Cache Buffer Chain的检索速度,当单个Chain超过一定长度后,系统会将原有的Chain断链,分裂组合成新的Chains。在这里,单个DBA的CR镜像分裂形成2条新Chain(Chain1、Chain2)。

2.4.4 Buffer Cache等待与优化

1. Buffer Cache等待事件

Buffer Cache等待事件主要分为3个级别:

❑ 用户级等待;

❑ 后台进程级(DBWR)等待;

❑ RAC集群级等待。

其中,DBWR后台进程等待事件产生于DBWR进程将Buffer写入磁盘以及扫描LRU List的等待,包含DBWR写操作超时、请求释放空间时扫描LRU List的时间消耗、Check-point检查点。

Buffer Cache常见等待事件有:

❑ buffer busy waits;

❑ free buffer waits;

❑ write complete waits。

2. Buffer Cache IO和DBWR优化

Buffer Cache的IO以及DBWR优化主要集在以下几个方面:

❑ 维持好的Buffer Cache命中率,一般要求大于90%,如下所示:

❑ 考虑采用高性能IO硬盘,如SSD硬盘灯,可以提升iops、tps等;

❑ 将物理存储条带化,使得物理IO均衡到多个磁盘,减少IO争用;

❑ 使用直接IO加载、直接插入操作,跳过Buferr Cache缓冲区,直接写入物理存储文件;

❑ 考虑适当调节参数_sort_multiblock_read_count/db_file_multiblock_read_count来提高IO性能,如下所示:

❑ 在特定场景中,考虑增加DBWR并行度,通过参数dbwr_io_slaves/db_writer_processes进行设置,如下所示:

3. Buffer Busy Waits(Free Buffer Waits)优化

(1)Buffer Busy Waits优化

Buffer Busy Waits产生的主要原因:

❑ 多个Session访问同一个Buffer Block;

❑ 多个Session等待同一个Buffer Block(Pin X)的更改提交(与事务相关)。

Buffer Busy Waits的解决办法如下:

❑ 考虑将热块数据分离到不同Block,例如,增加pctfree百分比;

❑ 对于索引热块的争用,可以考虑使用Reverse Index将热块分裂到不同的块;

❑ 优化数据存储方式,采用非顺序的方式随机将同一个块中的记录分散到不同数据库块中。

(2)Free Buffer Waits优化

Free Buffer Waits产生的主要原因:

❑ LRUW过满;

❑ DBWR进程不能及时快速清理LRUW List。

Free Buffer Watis的解决办法如下:

❑ 增加DBWR批量写大小,通过参数_db_large_dirty_queue进行设置,如下所示:

提示

参数_db_large_dirty_queue在10g中默认为30,在11g中默认为25。

❑ 采用SSD条带化磁盘,使得DBWR能够更快的写到磁盘;

❑ 使用异步IO,使用参数_dbwr_async_io进行设置,如下所示:

❑ 可以考虑适当减少LRU List的数量;

❑ 在使用多个LRUW List的时候使用多个DBWR进程;

❑ 确保“cache buffers lru chain latch”命中率高于99%,如下所示:

❑ 使用直接IO加载、直接插入操作,跳过Buffer Cache,提升DBWR写磁盘速度。

2.5 小结

本章主要介绍了Oracle Buffer Cache/Library Cache内存结构,同时结合Latch/Lock保护机制对内存结构知识进一步深化,帮助读者更好地理解掌握Oracle内存结构体系,为数据库优化、数据库故障排除提供技术支撑。

参考文档

[1] Jonathan Lewis, Oracle Core Essential Internals for DBAs and Developers(Appress)

[2] Steve Adams, Oracle8i Internal Services for Waits, Latches, Locks, and Memory(O'Reilly)

[3] JamesMore, Scaling Oracle8i(Apress)

[4] DSI405(Oracle Corporation)