Buffer Cache内部原理与I/O

2023年 10月 8日 60.6k 0

调优排故可以使用的等待事件、资料等内容大概介绍了一下。从本章开始,将分门别类地对这些资料、等待事件,以及它们所涉及的原理进行详细剖析。首先要分析的是Buffer Cache,因为它是Oracle中最重要的内存池之一。

阅读本章,需要对Oracle体系结构中的内存结构、SGA、Buffer Cache有基本的了解。如果还不知道Buffer Cache的作用、SGA各个部分的基础知识,需要先行阅读相关基础资料。

在开始介绍Buffer Cache内容前,先介绍两个名词,按照IT界的惯常叫法,对于一个块,在磁盘中叫Block(块);在内存中通常称为Buffer。后面将沿用这个习惯叫法。

在Buffer Cache中最重要、最难理解的部分就是它的各种链表,包括HASH链表、LRU链表、检查点队列链表等。因此本章将以链表为主线,挖掘Buffer Cache的工作原理。

3.1 哈希链表

作为磁盘数据块的缓存,Buffer Cache在Oracle的所有内存池中是最大的,几十GB的Buffer Cache比比皆是,偶尔也能遇到上百GB的Buffer Cache。这么大的空间Oracle是如何管理的呢?要准确回答这个问题,得首先从基本问题开始:假设进程要访问5号数据文件中的第1234号块,Oracle如何知道这个块是否在Buffer Cache中呢?如果在的话,它的具体地址是多少呢?

答案是:使用HASH算法。

3.1.1 HASH链表与逻辑读

如果使用得当,HASH算法是所有搜索算法中最快的。在Oracle中,几乎所有在内存中搜索数据的算法都采用HASH算法。如果对HASH算法不了解,可以先阅读一下本书的附录。

HASH算法中有一个重要的概念:Bucket。Buffer Cache中的HASH Bucket数量,由_db_block_hash_buckets参数设置。一般情况下,不需要修改此参数(后文会有一个关于此参数调节的案例)。

如果用图来表示,所有的HASH Bucket结构如图3-1所示。

图3-1 HASH表结构

当进程要读取某一个数据块时,比如,要读取5号文件中的第1234号块,它将根据文件号、块号来计算HASH值。假设此处计算出的HASH值为X,那么进程将根据此值,直接定位到Bucket X,如图3-2所示。

图3-2 HASH表定位

定位到这个Bucket后,就可以读取它里面的内容。它里面的内容如图3-3所示。

简单地说,在每个Bucket中,都只保存一个指向Cache Buffers Cache链表(简称CBC链表)的链表头。

先要简单说一下什么是链表。比如有3个人,张三、李四和王五,他们分别住在不同的地方。假设张三的住址是"中山路123号",李四的住址是"延安路456号",王五的住址是"长安街789号"。

图3-3 HASH表和Cache Buffers Cache链表

找一张纸,写上李四的住址"延安路456号",放在张三家中。这张纸,就是指针,它指向李四。这张纸放在张三家中,可以说张三指向李四。同理,将王五的地址放在李四家中,即"李四指向王五"。

这样一来,张三指向李四、李四指向王五。这就是单向链表。在数据结构中,张三、李四、王五被统一称为链表的Node(节点),本章后面也会使用这个统一的称呼:Node。

除单向链表外,当然还有双向链表。张三指向李四,李四指向张三;李四指向王五,王五指向李四,这就是双向链表。Oracle中所有的链表都是双向链表。

张三、李四、王五一旦形成链表,以后再找这3个人就方便了,不需要记住他们3个人的地址,只需记住一个人的地址就可以了。

将张三的地址"中山路123号",写到一张纸上,由于张三是链表中开头第一个人,因此这张纸就叫"指向链表头的指针"。

Oracle HASH表的Bucket中存放的是什么呢,就是“指向链表头的指针”。

假设张三、李四、王五他们几个都是记录Buffer Cache中Buffer的位置、状态等信息的,他们被称为Buffer Header(简称为BH)。

Buffer Cache中每一个Buffer都有一个Buffer Header,但是碰巧张三、李四、王五他们的HASH值一样,因此,他们几个人被组织成了链表,这个链表称为CBC链表。Buffer Cache的HASH表的Bucket中,存放的就是CBC链表的链表头。

如果已经学过HASH算法,可以知道这种情况就叫“HASH冲突”。所有的HASH算法,都无可避免HASH冲突的问题。解决HASH冲突问题的办法,就是在每个HASH Bucket后面建立一个链表。

现在既然已经找到Bucket X了,下一步当然就是读取它里面存放的CBC链表头,再接下来则是搜索链表了。进程将逐个比较每个BH中记录的文件号、块号,直到找到需要的为止。在图3-3中,Bucket X中第二个BH就是目标了。

接下来,再看一张图,如图3-4所示。

图3-4 逻辑读过程

图3-4中所画BH大小不同,这是因为后面要进一步扩展此图。实际上Oracle中每个BH的大小是固定的。

可以看到,在BH中有一项信息很重要,即BA(Buffer Address)。它是5号文件第1234号块在Buffer Cache中的地址。进程根据这个地址,直接访问Buffer就可以了。

通过这几张图,将进程在Buffer Cache中搜索Buffer的过程描述了一下。下面总结一下,搜索Buffer有以下几个步骤:

1)进程根据要访问块的文件号、块号,计算HASH值。

2)根据HASH值找到HASH Bucket。

3)搜索Bucket后的链表,查找哪个BH是目标BH。

4)找到目标BH,从中取出Buffer的BA。

5)按BA访问Buffer。

这个过程就是Oracle逻辑读的过程。如果搜索Bucket后的BH链表,没有找到包含目标文件号、块号的BH,那就证明Buffer Cache中不包含目标块,就只能物理读了。

3.1.2 Cache Buffers Chain Latch与Buffer Pin锁

SGA中是公共内存,哪怕要访问公共内存中的一个字节,都需要有某种锁机制保护。Oracle采用的锁机制就是Latch和Mutex。

在以上逻辑读的过程中,搜索Bucket后的链表,还有访问BH中的BA,都需要Latch的保护。这个Latch就是Cache Buffers Chain Latch(简称CBC Latch)。

基础较好的读者对前面的内容已经有所了解,下面更进一步来了解CBC Latch。为了让大家对CBC Latch有更形象的理解,这里用一幅图来表示,如图3-5所示。

图3-5 CBC Latch示意图

在图3-5中,Oracle在链表前加了一把锁,如果想访问链表,必须先申请获得这把锁。这个锁就是CBC Latch。

它不单保护对链表的访问,当在链表中找到目标BH时,有时还要对BH进行修改,修改的目的是为了加锁,此处的锁可以称为Buffer Pin锁。在修改完BH中Buffer Pin锁的状态后,CBC Latch就可以释放了。

之后,进程将在Buffer Pin锁的保护下访问Buffer。

总结一下,获得CBC Latch后,进程要完成两个工作:

1)搜索链表,查找目标BH。

2)修改BH中Buffer Pin锁的状态。

Buffer Pin锁有多种模式,最常见的有共享(用字母S表示)、独占(用字母X表示)两种模式。在没有加锁的时候,Buffer Pin锁的值将是0。

相关文章

Oracle如何使用授予和撤销权限的语法和示例
Awesome Project: 探索 MatrixOrigin 云原生分布式数据库
下载丨66页PDF,云和恩墨技术通讯(2024年7月刊)
社区版oceanbase安装
Oracle 导出CSV工具-sqluldr2
ETL数据集成丨快速将MySQL数据迁移至Doris数据库

发布评论