性能优化: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 条评论
登录 后参与评论

相关文章

来自专栏PPV课数据科学社区

SQL Server优化50法

查询速度慢的原因很多,常见如下几种: 1、没有索引或者没有用到索引(这是查询慢最常见的问题,是程序设计的缺陷) 2、I/O吞吐量小,形成了瓶颈效...

3107
来自专栏MYSQL轻松学

MySQL replace用法简介

今天在工作的过程中碰到一个问题,要把数据库中某个列的所有值中含有"ceshi.test.com"的字符去掉,本来可以写个脚本,把所有的值都取出再导入进行处理,但...

3459
来自专栏芋道源码1024

数据库中间件 MyCAT源码分析 —— XA分布式事务

---- 1. 概述 2. XA 概念 3. MyCAT 代码实现 3.1 JDBC Demo 代码 3.2 MyCAT 开启 XA 事务 3.3 MyCAT...

3129
来自专栏杨建荣的学习笔记

备库报警邮件的分析案例(二) (r7笔记第15天)

备库报警邮件的分析案例(一) (r7笔记第14天) 在第一篇中分享了关于备库报警邮件的分析,发现很多问题都是一环扣一环. 起初是通过监控主库中的v$datagu...

3275
来自专栏个人分享

Kudu存储实战笔记

  就像官网所说,Kudu是一个针对Apache hadoop 平台而开发的列式存储管理器,在本菜鸟看来,它是一种介于hdfs与hbase的一种存储。它的优势在...

693
来自专栏Python

Django-model进阶(中介模型,查询优化,extra,整体插入)

QuerySet 可切片 使用Python 的切片语法来限制查询集记录的数目 。它等同于SQL 的LIMIT 和OFFSET 子句。 1 >>>...

1877
来自专栏西安-晁州

ssh登录实现

工程目录 ? 配置文件详解 Spring的applicationContext.xml文件 <span ><?xml version="1.0" encodin...

2370
来自专栏前端儿

数据库初识--从MySQL 出发

要学Web 开发,也得先对数据库有所了解呀。数据库分门别类,多种多样,目前我选择了 MySQL 。

812
来自专栏杨建荣的学习笔记

海量数据迁移之外部表并行抽取(99天)

在10g开始的新特性中,外部表是一个不容忽视的好工具。对于大型项目中海量数据使用sqlloader是一种全新的方式,不过很明显,sqlloader的可扩展性更强...

3365
来自专栏Java技术栈

MySQL数据库开发的 36 条军规!

来自一线的实战经验,主要针对DBA和后端开发人员,总是在灾难发生后,才想起容灾的重要性;总是在吃过亏后,才记得曾经有人提醒过。文末是详细的视频讲解和PDF下载。...

735

扫描关注云+社区