性能优化:Cache Buffer Chain Latch等待事件

彭小波

ACOUG核心成员,Oracle用户组年轻专家。擅长Oracle数据库架构规划、SQL,OWI方面的优化。曾服务于各大企业数据库的维护以及系统开发,目前从事百年人寿保险股份有限公司的数据库服务与运维工作。

产生的背景

Oracle为了将物理 IO 最小化,把最近使用过的数据块保持在内存中。为了管理这些内存,oracle 使用如图的结构,Hash Chain 的结构,Hash Chain 位于共享池中,使用典型内存结构 Bucket->Chain->Header 结构进行管理 lock# 组成的,当扫描块时使用Hash函数进行hash运算,使用hash值查找 hash bucket,具有相同hash值的 buffer haeder 在 hash bucket 上以 chain 形式链接。Buffer header 有指向实际缓冲区的指针。注意:Hash Chain结构是在共享池中,而实际缓冲区信息存储在高速缓冲区中。Hash Chain 结构利用 cache buffers chain Latch来保护。

当进程扫描特定的数据块时,必须获得相应数据块所在 Hash Chain 管理的 cache buffers chain Latch。基本上一个进程获得仅有的一个 cache buffers chain Latch,一个 cache buffers chain Latch 管理多个 Hash Chain。当多个进程同时检索Buffer Cache时,获得 cache buffers chain Latch 的过程中发生争用,就会产生该等待事件。Hash Chain 结构的起点是Hash表,Hash 表由多个 hash bucket 组成,块地址是由 file#+b cache buffers chain Latch 等待事件。

进程扫描特定的数据块的过程如下: 1、Oracle 以每个块的文件号、块号和类型做 HASH 运算,得到 HASH 值。根据 HASH值,到 HASH 表中取出指定块的内存地址 2、获取 CBC Latch 3、根据 HASH 值,搜索 CBC 链表 4、根据 DBA 找到 BH(Buffer Header) 加 Buffer Pin 5、加完 Buffer Pin 马上释放 CBC Latch 6、访问 Buffer 开始 fetch 数据 7、获取 CBC Latch 8、释放 Buffer Pin 9、释放 CBC Latch

使用 SQL 语句可以获得 hash_latches,hash_buckets 数量,因此一个锁存器保护 Bucket数量是1048576/32768=32个。

SQL> select x.ksppinm name, y.ksppstvl value, y.ksppstdf isdefault, decode(bitand(y.ksppstvf, 7), 1, 'MODIFIED', 4, 'SYSTEM_MOD', 8 'FALSE') ismod, decode(bitand(y.ksppstvf, 2), 2, 'TRUE', 'FALSE') isadj from sys.x$ksppi x, sys.x$ksppcv y where x.inst_id = userenv('Instance') and y.inst_id = userenv('Instance') and x.indx = y.indx and x.ksppinm like '%db_block_hash%' order by translate(x.ksppinm, ' _', ' ');

产生的原因

1.执行效率低下的SQL,低效的 SQL 语句是发生 Latch:cache buffers chains 争用的主要原因。发生在多个进程同时扫描大范围的表或索引时。

2.出现热块 hot block 时,由于编写 SQL 语句时,SQL 持续扫描少数特定块(between and ,in,not in, exists),多个会话同时执行 SQL 语句时,发生 Latch:cache buffers chains 争用。

【案例1 Latch:cache buffers chains争用事件重现 】

--1.创建测试表

create table t1(id int,name varchar2(10)); insert into values(1,'xiaobo'); commit;

--2.获取t1表的第一行数据及 ROWID,根据 dbms_rowid 包查出这行数据的文件号、块号

SQL> select rowid, dbms_rowid.rowid_relative_fno(rowid) file#, dbms_rowid.rowid_block_number(rowid) block#, id, name from emm.t1 where rownum = 1; 2 3 4 5 6 7 ROWID FILE# BLOCK# ID NAME ------------------ -------- ---------- ----- ---------- AAADfaAAFAAAACDAAA 5 131 1 xiaobo

注意:这里的 DBA(Data Block Address) 就是由5号文件和131号块组成

--3.根据 DBA获取 CBC Latch 的地址

SQL> select hladdr from x$bh where file#=5 and dbablk=131; HLADDR ---------------- 00000001D1C266D8

--4.根据CBC Latch的地址可以查出这个 CBC Latch 被获得的次数

SQL> select addr,name,gets from v$latch_children where addr='00000001D1C266D8'; ADDR NAME GETS ---------------- --------- ------ 00000001D1C266D8 cache buffers chains 46

--5.再次读取t1表的第一行数据,再次产生一次逻辑读

SQL>select id,name from emm.t1 where rowid='AAADfaAAFAAAACDAAA'; ID    NAME ------- ------------ 1    xiaobo

--6.CBC Latch 的次数变为 48,说明一次逻辑读产生两次 CBC Latch

SQL> select addr,name,gets from v$latch_children where addr='00000001D1C266D8'; ADDR NAME GETS ---------------- ------------------------ ------- 00000001D1C266D8 cache buffers chains 48

这里说明一次逻辑读要加两次 CBC Latch,一次为了加 Buffer Pin,一次为了释放 Buffer Pin!

使用 oradebug 跟踪 CBC Latch 争用事件

SQL> oradebug setmypid Statement processed. SQL> oradebug peek 0x1D1C266D8 4 -- 观察 CBC Latch 地址为 0x1D1C266D8 开始之后的4字节信息的值为0 [1D1C266D8, 1D1C266DC) = 00000000 SQL> oradebug poke 0x1D1C266D8 4 1 --修改 CBC Latch 地址为 0x1D1C266D8 开始的4字节信息的值为1,相当于获取了 Latch BEFORE: [1D1C266D8, 1D1C266DC) = 00000000 --修改前的值 AFTER: [1D1C266D8, 1D1C266DC) = 00000001 --修改后的值

--7. 再开一个新的会话,会话号为768

SQL> conn / as sysdba Connected. SQL> select sid from v$mystat where rownum=1; SID ---------- 768

--8.在新会话768下再查询T1表的第一行,我观察到不会堵塞

SQL>select id,name from emm.t1 where rowid='AAADfaAAFAAAACDAAA'; ID   NAME -------- ------------ 1   xiaobo

--9.我们回到 oradebug 的会话,这次我们不是使用 select 语句,而使用 update 语句来获 取 latch

SQL> update emm.t1 set id=2 where rowid='AAADfaAAFAAAACDAAA'; 1 row updated.

--10.再次使用 oradebug 模拟获取 latch

SQL> oradebug setmypid Statement processed. SQL> oradebug poke 0x1D1C266D8 4 1 BEFORE: [1D1C266D8, 1D1C266DC) = 00000000 AFTER: [1D1C266D8, 1D1C266DC) = 00000001

--11.回到刚才768会话下,查询T1表的第一行,这时我观察到产生了堵塞

SQL>select id,name from emm.t1 where rowid='AAADfaAAFAAAACDAAA';

--12.我们再开第三个会话,查看会话号768的等待事件,我们看到产生了 CBC Latch 的等待事件

最后在第一个会话中释放 lacth

SQL> oradebug poke 0x1D1C266D8 4 0 BEFORE: [1D1C266D8, 1D1C266DC) = 00000001 AFTER: [1D1C266D8, 1D1C266DC) = 00000000

总结: 在获取保护 hash bucket 的 cache buffers chains latch 时,如果是读取工作(select),就以 shared 模式获得(这也是我们刚才在实验中 select 时没有产生争用的原因)。如果是修改工作(update),就以 exclusive 模式获得。

原文发布于微信公众号 - 数据和云(OraNews)

原文发表时间:2016-08-18

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏Golang语言社区

【Golang语言社区】GO1.9 map并发安全测试

var m sync.Map //全局 func maintest() { // 第一个 YongHuomap := make(map[st...

4778
来自专栏C#

DotNet加密方式解析--非对称加密

    新年新气象,也希望新年可以挣大钱。不管今年年底会不会跟去年一样,满怀抱负却又壮志未酬。(不过没事,我已为各位卜上一卦,卦象显示各位都能挣钱...)...

4928
来自专栏大内老A

The .NET of Tomorrow

Ed Charbeneau(http://developer.telerik.com/featured/the-net-of-tomorrow/) Exciti...

31810
来自专栏我和未来有约会

Kit 3D 更新

Kit3D is a 3D graphics engine written for Microsoft Silverlight. Kit3D was inita...

2576
来自专栏落花落雨不落叶

canvas画简单电路图

63111
来自专栏我和未来有约会

Silverlight第三方控件专题

这里我收集整理了目前网上silverlight第三方控件的专题,若果有所遗漏请告知我一下。 名称 简介 截图 telerik 商 RadC...

4045
来自专栏Ceph对象存储方案

Luminous版本PG 分布调优

Luminous版本开始新增的balancer模块在PG分布优化方面效果非常明显,操作也非常简便,强烈推荐各位在集群上线之前进行这一操作,能够极大的提升整个集群...

3185
来自专栏陈仁松博客

ASP.NET Core 'Microsoft.Win32.Registry' 错误修复

今天在发布Asp.net Core应用到Azure的时候出现错误InvalidOperationException: Cannot find compilati...

4868
来自专栏java 成神之路

使用 NIO 实现 echo 服务器

4717
来自专栏转载gongluck的CSDN博客

cocos2dx 打灰机

#include "GamePlane.h" #include "PlaneSprite.h" #include "BulletNode.h" #include...

5556

扫码关注云+社区