暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

Oracle读写数据块的过程

原创 graphy 2022-11-21
571

Oracle读写数据块的过程:
1)首先依据数据块地址计算出(HASH算法)数据块所在的HASH BUCKET。
2)根据桶的编号,计算出保护这个桶的CBC LATCH,然后申请CBC LATCH,找寻数据块在不在桶里(内存里),我们这里假设在内存里。
3)读取/修改数据块。
4)释放CBC LATCH。
以上的描述看似是非常通畅,但是存在一个问题,CBC LATCH的持有是排他的(我们暂时不考虑复杂情况:共享LATCH的持有情况),如果在排他持有CBC LATCH的情况下,读取数据块内容,那么这个LATCH的持有时间就会比较长,因为相对于LATCH的获取和释放这种CPU原子操作,读取数据块的内容是非常耗时的,因此在持有CBC LATCH的情况下,读取数据块,对于读写频繁的数据库/块,那么势必会造成CBC LATCH的争用(这个改进可以解决写不阻止读!)。为了解决这个问题,Oracle引入了buffer pin(buffer lock)的功能。
我们有必要对读取数据块的内容重新做下描述,大致步骤如下:
1)首先需要判断数据块所在的HASH BUCKET。
2)然后申请CBC LATCH,定位到数据块。
3)以S/X模式获取数据块的buffer pin/lock。(读取获得s模式,修改获得x模式,S和S模式具有兼容性,S和X、X和X模式不具有兼容性)。
4)释放CBC LATCH
5)在PIN的保护下,读取/修改数据块。(修改数据时pin一致在,锁是获取时申请。锁不是锁住而是申请锁。)
6)获得CBC LATCH。
7)释放(UNPIN)BUFFER PIN(BUFFER LOCK)。
8)释放CBC LATCH。
看似步骤复杂了,CBC LATCH获取/释放了两次,可是却大大的提高了并发度。上面描述的步骤里,持有CBC LATCH的目的变得单纯,只是为了修改BUFFER的PIN模式,然后依靠PIN的模式兼容性来保护数据块,例如:S和S模式的PIN是兼容的,可以并发的读取,S和X模式是不兼容的,后来的会话需要产生等待。
虽然LATCH的持有是排他的,但是这个时间极端,引起争用的可能性不大,如果大家都是来读数据块的,那么BUFFER LOCK的S模式之间都是具有共享性的,不会产生争用。但是同一个时刻,如果一个进程以S模式持有了数据块的BUFFER LOCK,另一个进程想以X模式持有,那么就会出现争用,因为道理很简单,S模式的BUFFER LOCK和X模式的BUFFER LOCK不兼容。同理,两个同时欲修改同一个数据块的进程,也会遭遇BUFFER LOCK冲突.这个冲突以ORACLE 等待事件表示出来就是Buffer Busy Waits,也就是说Buffer Busy Waits等待的本质是buffer lock的争用导致的。
我们平时经常说读不阻塞写,写不阻塞读,那是在物理的数据块级别,在内存里,读写/写写在同一个时刻都是互相阻塞的。只有读读不阻塞。
为了方便理解,上面很多步骤做了简化,下面对某些点做些补充:
1)一旦你PIN住了一个数据块,不需要立即去UNPIN(移除PIN)它。ORACLE认为你的本次调用后还有可能去访问这个数据块,因此保留了PIN,直到本次调用结束再UNPIN。
2)Oracle在对唯一索引/undo块/唯一索引的回表/索引root、branch块的设计上,在访问(读取)的时候,获取的是共享的CBC LATCH,不需要去PIN数据块,在持有共享CBC LATCH的情况下读取数据块。可能的原因是这些块修改的可能性比较小,因此Oracle单独的采用这种机制。因此对于普通数据块的读取都是需要获取2次CBC LACTH,而对于这种特殊的数据块,只获取一次共享CBC LATCH就OK 了。
3)我们上面所说的情况都是在数据块已经存在在内存里的情况。如果数据块不在内存,有可能会产生READ BY OTHER SESSION争用等待。
4)上面描述只符合10G后的版本。在10G前读读也会产生BUFFER BUSY WAITS,10G后把这方面的BUFFER BUSY WAITS归到了READ BY OTHER SESSION等待里。
5)上面的描述基本都采用了数据块这个词,更准确的表达应该是buffer block。(oracle就算只改一行,也是操作整个块的)

1. ORACLE 访问Data buffer block的过程:
1)依据数据块的地址计算出数据块所在的bucket
2)获得保护这个bucket的cbc latch
3)在这个链表上找寻我们需要的数据块(没有的话需要物理IO),找到后,pin这个buffer(读取s,修改x)
4)释放cbc latch
5)读取/修改数据块的内容
6)获取cbc latch
7)unpin这个buffer
8)释放cbc latch

两次需要latch 只是用来pin和unpin

2.buffer busy wait 等待事件形成原因:
(1). 读读
当一个session 读取一个block时,该block在cache中不存在,需要从disk读取至cache,此时其他session 想要读取该block时,就会发生buffer busy wait 等待事件。

(2). 写写
当一个session 修改block时,首先会对该block 进行buffer pin(修改x),因此其他session在对该block进行修改时都要等待上一个session unpin buffer。

(3). 读写
1)当读取的进程发现内存块正在被修改的时候(如果有x模式的buffer pin,就说明正在被修改),它只能等待,它不能clone块,因为这个时候内存块正在变化过程中ing,这个时候clone是不安全的。很多人说,oracle里读写是互相不阻塞的,oracle可以clone内存块,把读写的竞争分开。其实要看情况,在读的时候发现内存块正在被写,是不能够clone的,因为是不安全的。这个时候读的进程只能等待buffer busy waits。
2)当写的进程发现内存块正在被读,这个时候,读是不阻塞写的,因为ORACLE可以很容易的clone出一个xcur的数据块,然后在clone的块上进行写,这个时候clone是安全的,因为读内存块的进程不会去修改数据块,保证了clone的安全性。

(4).

 




3.buffer busy wait 常见发生原因
(1). 性能差的QUERY访问相同的block 并发执行时,产生大量的物理读。
(2). freelist 设置过小,导致并发insert table时,频繁扫描freelist,产生争用。-已过期
(3). 大量session 并发修改相同的index block

4.常用解决方法
(1). tuning sql 减少物理读
(2). 删除一些hot row 并重新insert 至其他block中--- 不能保证啊,不过如果块多可以平均分配
(3). 如果table 较小,可以考虑将其数据 cache至keep data buffer中
(4). 减少low cardinality index的使用。 避免index block 争用。 --低基数就是大量重复的,记录在一个B+树的叶子节点上
(5). 增加extents size。 避免oracle频繁分配空间而造成的extent map 争用。

新创建的表,还没分配任何区,如果此时allocate手动分配区大小64k,8个block,刚好初始一个区的大小。不会分配1个区,而是2个区。即第一个区是初始区,默认还会再分配一个。
allocate,默认会先分配一个段头,8个block,然后会按128个block进行分配

1.allocate是为段手动分配区的命令。一般默认情况下,当我们使用完一个区的时候,oracle会自动为我们再分配一个区。
2、我们可以在插入大量数据时,可以手动分配一下区,这样在插入的时候节省了分配区的时间,能改善Oracle的性能,提高插入的速度。
3、我们可以对以下数据表进行统计分析,分析一些插入数据频繁的表,估算出一个周期的增长量,根据增长量,我们预先分配好区,来提升性能。
4、allocate手动指定了分配的区总大小,但是每个区的大小,默认还是由oracle决定,即还是遵循系统管理区大小原则。


alter table zhuo.t1 allocate extent(size 1G);

6 pct free pct used 设置。

5. 产生等待的block 类型及解决方法

Block Type Possible Actions
data blocks Eliminate HOT blocks from the application. Check for repeatedly scanned / unselective indexes. Change PCTFREE and/or PCTUSED. Check for 'right- hand-indexes' (indexes that get inserted into at the same point by many processes). Increase INITRANS. Reduce the number of rows per block.
segment header Increase of number of FREELISTs. Use FREELIST GROUPs (even in single instance this can make a difference).
freelist blocks Add more FREELISTS. In case of Parallel Server make sure that each instance has its own FREELIST GROUP(s).
undo header Add more rollback segments.
6.相关命令

SELECT p1 "File", p2 "Block", p3 "Reason"
FROM v$session_wait
WHERE event='enq: TX - row lock contention'; buffer busy wait

select objd, file#,block#,class#,ts#,cachehint,status,dirty from v$bh where file#=546 and block#=1289912;

select * from dba_objects where object_id = 124269

select current_obj#,obj.object_name,count(*) from dba_hist_active_sess_history , dba_objects obj
where event='buffer busy waits'
and sample_time>=to_date('2027-07-09 00:00:00','yyyy-mm-dd hh24:mi:ss')
and sample_time<=to_date('2027-07-10 00:00:00','yyyy-mm-dd hh24:mi:ss')
and obj.data_object_id = current_obj#
group by current_obj#,obj.object_name

「喜欢这篇文章,您的关注和赞赏是给作者最好的鼓励」
关注作者
【版权声明】本文为墨天轮用户原创内容,转载时必须标注文章的来源(墨天轮),文章链接,文章作者等基本信息,否则作者和墨天轮有权追究责任。如果您发现墨天轮中有涉嫌抄袭或者侵权的内容,欢迎发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论

暂无图片
获得了188次点赞
暂无图片
内容获得72次评论
暂无图片
获得了369次收藏