buffer busy waits 等待事件的产生

当n个进程想以不兼容的模式持有内存块上的buffer pin的时候,就会产生buffer busy waits等待

什么?
内存块上有buffer pin ?
不是说内存块的锁都是靠latch实现的吗?什么时候还冒出一个buffer pin?从来没听说过!!
好,既然你这么问,那我们可以先假设没有buffer pin这东东,看看oracle怎么去访问/修改一个数据块。(下面的过程尽可能的我做了简化)
1)依据数据块的地址计算出数据块所在的bucket
2)获得保护这个bucket的cbc latch
3)在这个链表上找寻我们需要的数据块
4)读取/修改数据块的内容
5)释放cbc latch
我们知道latch的获得和释放时间一般都是极短的(cpu的原子操作),上面5个步骤里1,2,3,5的时间我们都可以认为是极快的操作。
但是步骤四消耗的时间相对于这几个就大了去了。我粗糙的画了一个图,可以参展一下

这样就导致了一个问题,在大并发环境下,由于cbc latch的持有时间过长,会导致大量的latch争用,latch的大量争用非常容易导致系统的cpu资源出现瓶颈。需要特别说明的是,即使你所有的操作都是查询非修改,也会导致大量的cbc latch争用:cbc latch的持有到cbc latch的释放这段时间过长了。
如何解决这个问题呢,说一下ORACLE的做法。
ORACLE通过让每次访问buffer block的会话获取两次cbc latch,再配合在内存块上加buffer pin来解决这个问题

看如下的步骤。
1)依据数据块的地址计算出数据块所在的bucket
2)获得保护这个bucket的cbc latch
3)在这个链表上找寻我们需要的数据块,找到后,pin这个buffer(读取s,修改x)
4)释放cbc latch
5)读取/修改数据块的内容
6)获取cbc latch
7)unpin这个buffer
8)释放cbc latch
通过这种实现方式,我们看到cbc latch的持有时间大大降低了,因为cbc latch的持有,只做了很少的事情,这样就大大降低了cbc latch的争用。
你可能会挑战说,虽然cbc latch的争用会大大减轻,可是ORACLE只不过是转移了竞争点,现在变成了buffer lock之间的竞争。
你说的对,但是也不对!!
如果你的数据库里读极多,写极少,由于各个读之间的buffer pin是兼容的,都是s模式,因此不会产生任何的争用。
如果你的数据库里写极多,读极小,就会产生buffer busy waits等待,但是这种等待的代价比cbc latch的等待代价要小的多,latch的spin机制是非常耗cpu的,而buffer pin的管理本质上类似于enq 锁的机制,没有spin机制,不需要自旋耗费大量的cpu。
如果你的数据库是读写混合的场景,那么写会阻塞读,产生buffer busy waits,但是读不会阻塞写,不会产生这个等待。这个我们后面会重点讨论

下面我们模拟下怎么产生busy buffer waits等待事件:

SCOTT@orcl>select dbms_rowid.ROWID_RELATIVE_FNO(rowid) fn, dbms_rowid.rowid_block_number(rowid) bl, rowid from emp ;

    FN         BL ROWID

     4        151 AAAR3xAAEAAAACXAAA
     4        151 AAAR3xAAEAAAACXAAB
     4        151 AAAR3xAAEAAAACXAAC
     4        151 AAAR3xAAEAAAACXAAD
     4        151 AAAR3xAAEAAAACXAAE
     4        151 AAAR3xAAEAAAACXAAF
     4        151 AAAR3xAAEAAAACXAAG
     4        151 AAAR3xAAEAAAACXAAH
     4        151 AAAR3xAAEAAAACXAAI
     4        151 AAAR3xAAEAAAACXAAJ
     4        151 AAAR3xAAEAAAACXAAK

    FN         BL ROWID

     4        151 AAAR3xAAEAAAACXAAL
     4        151 AAAR3xAAEAAAACXAAM
     4        151 AAAR3xAAEAAAACXAAN

可以看到,emp表上的所有行都是在file 4,block 151上面的

现在我们来模拟三种情况:

情况一:两个session并发的进行查询

SCOTT@orcl>select distinct sid from v$mystat;

   SID

   164

SCOTT@orcl>declare
2 c number;
3 begin
4 for i in 1 ..6000000 loop
5 select count(*) into c from emp where empno=7788;
6 end loop;
7 end;
8 /

SCOTT@orcl>select distinct sid from v$mystat;

   SID

   422

SCOTT@orcl>declare
2 c number;
3 begin
4 for i in 1 ..6000000 loop
5 select count(*) into c from emp where empno=7900;
6 end loop;
7 end;
8 /

这个时候查询当前的非空等待事件:

SYS@orcl>select distinct event,s.wait_class from v$session_wait s,v$event_name e where event=name and e.wait_class not like 'Idle';

EVENT WAIT_CLASS


SQL*Net message to client Network

可以看到没有buffer busy waits的等待事件

情况二:一个session进行查询,另一个session进行更新操作:

SCOTT@orcl>select distinct sid from v$mystat;

   SID

   422

SCOTT@orcl>
SCOTT@orcl>begin
2 for i in 1 ..4000000 loop
3 UPDATE emp SET sal=2000 where empno=7788;
4 commit;
5 end loop;
6 end;
7 /

SCOTT@orcl>select distinct sid from v$mystat;

   SID

    85

SCOTT@orcl>declare
2 c number;
3 begin
4 for i in 1 ..6000000 loop
5 select count(*) into c from emp where empno=7900;
6 end loop;
7 end;
8 /

在这样的情况下系统的等待事件:

SYS@orcl>select distinct event,s.wait_class from v$session_wait s,v$event_name e where event=name and e.wait_class not like 'Idle';

EVENT WAIT_CLASS


latch free Other
log file parallel write System I/O
control file parallel write System I/O
SQL*Net message to client Network

情况三:两个session同时并发的进行更新操作

session 1:

SCOTT@orcl>begin
2 for i in 1 ..4000000 loop
3 UPDATE emp SET sal=2000 where empno=7788;
4 commit;
5 end loop;
6 end;
7 /

session 2:

SCOTT@orcl>begin
2 for i in 1 ..4000000 loop
3 UPDATE emp SET sal=2000 where empno=7900;
4 commit;
5 end loop;
6 end;
7 /
查询系统的当前等待事件:

EVENT WAIT_CLASS


log file switch (checkpoint in Configuration
complete)

control file parallel write System I/O
buffer busy waits Concurrency
SQL*Net message to client Network

看到了buffer busy waits的等待事件

我们看到发生写的会话session 1,没有任何的buffer busy waits等待,而发生读的会话session 2,产生了大量的buffer busy waits等待。
网上对这一块的争论是比较激烈的。
道理其实非常简单
1)当读取的进程发现内存块正在被修改的时候(如果有x模式的buffer pin,就说明正在被修改),它只能等待,它不能clone块,因为这个时候内存块正在变化过程中ing,这个时候clone是不安全的。很多人说,oracle里读写是互相不阻塞的,oracle可以clone内存块,把读写的竞争分开。其实要看情况,在读的时候发现内存块正在被写,是不能够clone的,因为是不安全的。这个时候读的进程只能等待buffer busy waits。
2)当写的进程发现内存块正在被读,这个时候,读是不阻塞写的,因为ORACLE可以很容易的clone出一个xcur的数据块,然后在clone的块上进行写,这个时候clone是安全的,因为读内存块的进程不会去修改数据块,保证了clone的安全性。

说到这里,基本上可以来一个简单的总结了,但是总结前,还是有必要给大家简单介绍一下,buffer header上的两个列表

每个buffer header上都有2个列表:users list和waiter list。
users list用来记录,当前有哪些会话获得了此buffer block上的buffer pin,并记录下buffer pin的模式。
waiter list用来记录,当前有哪些会话在等待buffer block 上的buffer pin,并记录下申请buffer pin的模式。
看到这两个列表,是不是觉得似曾相识?对了,enq锁的管理也是类似的这个方式,不过多了一个列表,锁转换列表。
给大家举个例子,会更清晰一些:
session 1(sid 123):修改数据块block 1
此block的buffer headler上的users list如下:
sid hold mode
123 x

session 2(sid 134):也想修改数据块block 1,但是由于于session 1的锁模式不兼容,只能等待buffer busy waits,此时的user list不变,waiter list如下:
sid req mode
134 x

session 3(sid 156):也想修改数据块block 1,但是由于于session 1的锁模式不兼容,只能等待buffer busy waits,如果这个时候我们去观察后台的等待,会发现2个会话在等待buffer busy waits了(134,156)。此时的user list不变,waiter list如下:
sid req mode
134 x
156 x

如果这个时候sid为123的会话修改完成,那么会通知sid为134的会话获得buffer pin,此时的user list,waiter list 如下:
user list
sid hold mode
134 x

waiter list
sid req mode
156 x

可要看到,buffer lock的这种机制非常类似于enq锁的机制,先进先出,然后通过n个列表来记录锁的拥有者和等待着。等待buffer busy waits的进程在进入队列后,会设置一个1秒(_buffer_busy_wait_timeout)的超时时间,等待超时后,会“出队”检查锁有没有释放,然后重新入队。

最后我们可以来一个总结了:
1)buffer busy waits是产生在buffer block上的等待,由于n个进程想以不兼容的模式获得buffer block的buffer pin,进而引起buffer busy waits等待。
2)buffer lock的管理模式非常类似enq锁的管理模式,先进先出,有队列去记录锁的拥有者和等待着。
3)写写,读写都会产生buffer busy wiats等待。写写的两个会话,都会产生buffer busy wiaits等待,而读写的两个会话,只有读的session会产生,因为它不能去简单的clone一个内存块,正在发生写的内存块发生克隆是不安全的
4)oracle为了解决cbc latch持有时间过长的问题,以每次访问buffer block的会话获取两次cbc latch,再配合在内存块上加buffer pin来解决这个问题。

说明:oracle并不是针对所有的内存块都采取两次获取cbc latch的机制,比如针对索引root,索引branch,唯一索引的叶子节点,都是采取的一次获取机制

buffer busy waits的处理步骤:

1、定位当前buffer busy waits的热块:

select event,p1,p1text,p2,p2text,p3,p3text from v$session_wait where wait_class not like 'Idle';
EVENT P1 P1TEXT P2 P2TEXT P3 P3TEXT


latch: cache buffers chains 2.5253E+10 address 150 number 0 tries
direct path read 7 file number 1581054 first dba 1 block cnt
direct path read 7 file number 1609013 first dba 4 block cnt
log file parallel write 1 files 18 blocks 1 requests
direct path read 7 file number 1607515 first dba 1 block cnt
SQL*Net message to client 1650815232 driver id 1 #bytes 0
buffer busy waits 4 file# 151 block# 1 class#
Disk file operations I/O 3 FileOperation 0 fileno 4 filetype

我们可以看到,造成buffer busy waits热块的是file 4 block 151这个数据块

下一步我们再来查找这个数据块是属于哪个段的

Select owner, segment_name, segment_type, partition_name,tablespace_name from dba_extents
where relative_fno='&filename' and '&blocknum' between block_id and (block_id+blocks-1)
OWNER SEGMENT_NAME SEGMENT_TYPE PARTITION_NAME TABLESPACE_NAME


SCOTT EMP TABLE
————————————————
版权声明:本文为CSDN博主「bigclouder」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/kiwi_kid/article/details/25799419

转载:http://blog.itpub.net/29475508/viewspace-1256685/

【实验过程】
1、主、备库查看当前日志状况

select sequence#,status from v$log;

2、备库查看RFS接收日志和MRP应用日志同步主库情况

select process, sequence#, status, delay_mins from v$managed_standby;

3、察看备库是否和主库同步

select sequence#,status from v$log;
select archived_thread#,archived_seq#,applied_thread#,applied_seq# from v$archive_dest_status;

4、备库查4、看已经归档的redo

select REGISTRAR,CREATOR,THREAD#,APPLIED,sequence#,first_change#,next_change#, COMPLETION_TIME from v$archived_log;

5、备库查看已经应用的redo

select thread#,sequence#,first_change#,next_change# from v$log_history;

6、主、备库查看是否有gap

select status,gap_status from v$archive_dest_status where dest_id=2;

备库查看

select * from v$archive_gap;

通过上面这6种方法,都可以对主备库间日志传输情况进行监控。
如果日志传输出现代沟(GAP),就需要及时排查问题,必要时还可以采取手工处理GAP措施。

Oracle RHP

转载:http://atoracle.cn/index.php?m=Article&a=show&id=371

随着IT信息化的发展。现在数据中心的规模越来越大,对管理员的要求也越来越高。同时,用户希望快速访问始终在线的服务,因此对于企业来说,部署和维护必须高效且对业务运行无干扰。为了跟上步伐,必须减少运维复杂性和手动参与的步骤。Oracle RHP (Rapid Home Provisioning) 的解决方案标准化、简化了软件分发和管理。自动化和高效率是她的特点,她最小化了对大规模部署的影响。

Rapid Home Provisioning (RHP) 代表了一种标准的方法,以统一的方式,在软件基础设施的所有体系结构层 (Oracle Database和其他第三方定制软件) 上进行部署、补丁、升级、迁移等工作,尤其是Oracle集群、数据库的部署、升级、补丁、迁移,以及集群节点的伸缩等操作非常便捷。

转载:https://www.modb.pro/db/101569?xzs=
清理Oracle大表

1、查看表的大小

SQL>select owner,
       segment_name,
       segment_type,
       tablespace_name,
       round(bytes / 1024 / 1024 / 1024, 0) GB
  from dba_segments
 where segment_name='TEST';

OWNER   SEGMENT_NAME  SEGMENT_TYPE   TABLESPACE_NAME     GB
------- ------------  -------         ----------------- ----
SCOTT    TEST          TABLE             USERS           10

2、获取表的定义

set long 999
SQL>select dbms_metadata.get_ddl('TABLE',upper('&i_table_name'),upper('&i_owner')) from dual;

3、查看表的依赖关系

SQL>select * from user_dependencies t where t.referenced_name = 'TEST';

4、查看对象的状态:

SQL>select owner, object_name, object_type, status from dba_objects where object_name in ('TEST_PKG','TEST1_PKG');

5、将表重命名

SQL>alter table TEST rename to TEST_B;

6、根据抽取的表的定义,重建新表

7、查看失效的对象

SQL>select owner, object_name, object_type, status from dba_objects where object_name in ('TEST_PKG','TEST1_PKG')

8、重新编译对象:

SQL>select 'ALTER ' ||
       decode(object_type, 'PACKAGE BODY', 'PACKAGE', object_type) || ' ' ||
       owner || '.' || OBJECT_NAME ||
       decode(object_type,
              'PACKAGE BODY',
              ' COMPILE BODY ; ',
              'PACKAGE',
              ' COMPILE SPECIFICATION ; ',
              ' COMPILE; ') aa
  from dba_objects
 where status <> 'VALID'
   and dba_objects.owner in ('SCOTT')
   AND object_name in ('TEST_PKG','TEST1_PKG');

生成如下的编译脚本:执行编译脚本

ALTER PACKAGE SCOTT.TEST_PKG COMPILE BODY ; 
ALTER PACKAGE SCOTT.TEST1_PKG COMPILE BODY ; 

9、清理旧表:TEST_B

SQL>truncate table TEST_B reuse storage;

分批释放大小:

SQL>ALTER table TEST_B DEALLOCATE UNUSED KEEP 8G;
SQL>ALTER table TEST_B DEALLOCATE UNUSED KEEP 6G;
SQL>ALTER table TEST_B DEALLOCATE UNUSED KEEP 4G;
SQL>ALTER table TEST_B DEALLOCATE UNUSED KEEP 2G;
SQL>ALTER table TEST_B DEALLOCATE UNUSED KEEP 0G;

查看释放后的表的大小:

SQL>select owner,
       segment_name,
       segment_type,
       tablespace_name,
       round(bytes / 1024 / 1024 / 1024, 0) GB
  from dba_segments
 where segment_name='TEST_B';

10、删除旧表

SQL>drop table TEST_B purge;