oracle学习笔记 buffer_cache作用概述

时间:2022-07-01 21:13:27


oracle学习笔记  buffer_cache作用概述


从这节课开始讲buffercache
对oracle数据库来讲最重要的内存结构是buffercache
buffercache的合理使用它直接关系到数据库运行的性能
对DBA来讲数据库的性能是非常重要的


和性能即对立又相附的是安全
oracle数据库的数据安全性一致性有时会出现问题
我们掌握基本的一些概念和手法可以避免
当然有好多知识需要去学习
oracle数据库的一致性一旦出问题就是灾难性的
很多时候把数据搞得不一致了老师也没有很好的办法
只能一点点的验证,很麻烦


不一致简单讲就是数据库中数据的值和真实值不一样了
可人为造成,也可是数据库运行时由软件或硬件造成
就是数据出错了
对一个大型的库来讲想排除一个这样的错误是非常麻烦的,
因为你可能根本不知道它已经出错了
或者根本不知道它错在哪里
即使找到了地方也有可能根本不知道正确的值是什么
对于一些不稳定的配置是极易出现这种问题的
如一些"先进"的软硬件配置
因为先进设备带来的最大副作用就是不稳定也就是说经常出错
尽管一些对软硬件的修改未必是什么新的技术,说成是先进很勉强
但作用是肯定有的,原软件稳定性差了
想保证一个数据库系统的安全首要的任务是使用安全的软硬件设施
这样数据的一致才有最好的保证


但是我们平时工作中最主要的时间和精力花费在oracle数据库的优化上
优化是让数据库运行的更快更稳定
优化是从一个很高的高度来设置数据库
但我们首要的任务应该是得到一个安全稳定的基础


我们在优化过程中非常注意关注的一个地方就是buffercache


这节课主要讲一下buffercache的工作机制和工作原理
当然我们后面会给大家讲一些操作和sql语句
让大家更深一步的去理解这些概念和原理


今天我们就从buffercache最基本的讲起


一)buffer_cache作用概述


先讲一个最基础的东西
段、区、块的概念


1)数据库基本功能和结构


我们需要数据库是因为它可以
1、存储数据
但并不是我们最终使用的目的
对oracle数据库来讲它存储数据的同时我们可以很方便的
2、检索和处理数据
处理里面包括
增加数据、删除数据、修改数据

举个很简单的例子
假设有个公司有十万个员工
我们把十万个员工所有的信息全部存储在数据库里面去
将来要检索要找某个员工信息的时候非常容易
数据库里面一条sql语句一下就出来啦


但如果说你不用数据库你用别的格式的文档去存储这个信息的话
你找的时候要翻半天
但数据库不是
它以存储数据为手段
最主要的是要检索以及处理数据
这是我们处理数据库的目的


数据库里面存储的数据其实就是表
表是一个有行列的二维结构


比如说一个关于人员的一个表,员工employee表
表里有员工编号、员工姓名、性别、出生日期,
还有些别的信息如家庭住址、电话号码等等
这就是一张表
oracle数据库里面就是存储着几十张几百张表
一般老师做的数据库里面多是四五百张以上
可以这么认为oracle数据库里面存储着表
平时可以检索表数据,同时也可以对表数据进行处理
当然了我们为了检索处理数据
除了表以外在数据库里面还建了一堆索引、存储过程、函数、视图、序列包括物化视图等等
都是为了配合我们对数据库的数据的检索以及处理而产生的一些对象
但是对数据库来讲最基础的我们最关心的最关注的就是表


数据库里面存储的是表
表就是实实在在的这么一张表


oracle数据库的体系结构里有
控制文件、redolog文件、dbf数据文件
数据文件里面放的很简单是表


dbf文件有它的结构组织
只要这个dbf文件属于oracle数据库就被分成一个一个大小相等的块
大小可以是通常的8K
可以是4K、2K也可以是16K、32K
一般的都是8K


可以看一下我们使用的数据库块的大小


老师在课程中使用了
export NLS_LANG=american_america.zhs16gbk
是因为它的环境中参数NLS_LANG设置有问题
导致很多字符输出的乱码,
而我的环境变量已设置正确不会出现字符输出乱码
如果已经设置正确了不用去理会。
前面在软件安装准备工作中讲过
oracle用户此环境变量可在此用户的 .bash_profile文件中设置


关于数据库块的环境变量
SQL> show parameter block

NAME                                 TYPE        VALUE
------------------------------------ ----------- ------------------------------
db_block_buffers                     integer     0
db_block_checking                    string      FALSE
db_block_checksum                    string      TRUE
db_block_size                        integer     8192
db_file_multiblock_read_count        integer     16

有这么一行
db_block_size                        integer     8192
说明当前数据块的大小是8K


oracle数据库里面dbf文件被分割成了无数个8K大小的块
在dbf拿出一个来时就是一个block


oracle的块block是oracle的io的最小单位


2)段、区、块的概念


段可以这样认为:一个表就是一个段


建一个表
SQL> create table t2(id int,name varchar2(20));

Table created.

对oracle数据库来讲建立了一个表t2
在oracle数据库里它自然给你建一个段,段名t2


我们讲分区表会有很多段现在不要想这么多
现在姑且认为一个表一个段


然后可以插入数据
SQL> insert into t2 values(1,'xkj');

1 row created.

SQL> insert into t2 values (2,'jiagulun');

1 row created.

SQL> commit;

Commit complete.

刚才给演示的过程在oracle数据库里面的体现


首先来讲建了一个表t2
oracle数据库建了一个段t2
在dbf文件里面
oracle为t2分配一个区


一个表就是一个段
你建了一个表同时建了一个段
段一旦建了以后
oracle做的第一件事情给一个段分配一个区
区的英文extent(区域)
区的概念是物理上连续的多个块


这里比如8个块做为一个区给t2这个段
这个区是8个连续的oracle块全部属于t2这个段


建完以后我们开始插入数据
段的前几个块被段自己使用了,数据部分从后面开始
insert into开始插入一行行数据
插入数据时oracle会
找一个空块然后把数据一行行的写进去
写满了这个块以后
接着写第二个块、第三个块
第八个块写满了的时候
我们给它分配的区已经用满了
区既然都用完了oracle接着再分配一个区
还是八个块
这时候这个段又有了新的一个区又有了8个块可用
接着再插入数据
再用完了再给它分配一个区


建表自然就建了一个段
接着给这个段分配一个区
oracle就可以使用区里面的一些空块
用完后再分配新区


区是oracle给段分配空间的最小单位
也就是oracle给段分配空间的时候一次性分配一个区
不是分配一个块是一次性分配一个区

3)
块是oracle I/O的最小单位

dbf文件上有好多块
表t2在这些块里面
现在我要访问t2
要访问t2里面的某个块
块里面放的是数据行
原则上来讲一个块里面放多个行
一般的情况不会出现一个行在多个块里面


但在发生了行链接、行迁移行为和保存数据类型为long或lob的情况下
可能会出现一个行
在一个块里面有点在另外一个块里也有点


到目前为止就认为一个块里面有多个行


比如我要访问某个行
select * from t2 where id=10;
oracle计算发现要访问的这个行在某个块里面
这个块里面有20行,但你只访问其中一行
这时oracle不会只读其中一行
oracle会向磁盘发出I/O请求去请求一个块
这时操作系统会把整个块读到内存里面去
然后cpu会在这个块里面找到我们需要的那一行
把这一行读出来返给用户
oracle IO的最小单位是块是oracle块
就是db_block_size值确定的大小的块


有人说了oracle太笨了
你只是要读其中一行数据
假设这一行所在块8K
这一行只有400个300个字节
为了读300个字节把整个块全部读到内存里面去
大家认为oracle很笨


其实原因又回到我们以前的缓存的概念
和我们讲的物理硬盘的工作原理


对硬盘来讲一次IO里面最主要的是寻道时间
一次寻道甚至占到一次IO的90%的时间
我们要读这个块至少要发生一次寻道
然后我们把整个块读出来


如发生了寻道花了10ms
读一行需要花1ms,读块中所有20行需要花1.2毫秒
差不多这个时间比例
1.2和1相对10ms来讲都很小
所以我把整个块读到内存里面去合适
还有一个概念
oracle读了这一行以后
接着再读这一行上面的和下面的行几率是很大的
就是我把整个块读到内存去以后
有可能在接下来的10个8个读里面oracle都会读这个块里面的内容
所以oracle的io的最小单位是块而不是行



讲到目前为止讲了两个概念
1、讲了段区块的概念
区是oralce给段分配空间的最小单位
区是物理上连续的几个oracle块
2、块是oracle IO的最小单位
oracle要从dbf里面取一行十行
这时oracle都是以块为单位去发出io请求的
io的最小单位是块


二)buffercache的两个意义


1)block和buffer


oracle的内存结构中SGA内存中
有sharedpool、logbuffer
最大的是buffercache


硬盘中有
控制文件 和 dbf 和 logbuffer


sharedpool里面存储的主要是sql语句和执行计划以及我们的数据字典
logbuffer里面存的是日志


buffercache和dbf是对应的
dbf的数据会被调到buffercache里面
也就是buffercache缓存的是dbf的数据
dbf文件都被分成了大小相等的多个block块大小为8K
自然buffercache也要被划成一个一个的块我们给它起名叫buffer


所以在dbf里面一个块就一个block
内存里面块叫一个buffer
block和buffer是一一对应的
都是8K


2)buffercache第一个意义


buffercache最主要的意义有两个


第一个意义缓存dbf文件


我们要访问t2的某一行
比如t2的第11行
这时oracle经过计算以后发现t2的第11行在某个块里面
而oracle IO的最小单位是block
这时oracle 发出一个IO请求
传达给操作系统,操作系统传给文件系统,文件系统传达到磁盘上
最终这个块被调入到内存里面去成了一个buffer


dbf里的块block和buffercache里面的buffer对应了
然后oracle就从这个buffer块里面把11行取出来
这个时候我们说oracle发生了一次物理的io,从磁盘到内存


接着oracle要读t2的第12行
oracle要读一个表的时候首先到buffercache里面去找
看看表对应的块有没有在内存里面


oracle要读一个表就是访问一个段
这时候oracle有能力计算出来
要访问的这个表的这个行所对应的这个段
这个行在这个段的那个块里面


oracle要读t2的11行的时候
oracle发现它需要读这个段的比如说第8个块
到内存里找
发现这个段的第8个块没有在内存里
这时候就认为在磁盘上
就到磁盘上去把这个块读到内存里面去
读进来以后然后再把11行读出来
接着oracle读12行
读第12行的时候
oracle发现第12行是段的第8个块
在内存里面找正好在内存里面找到了这个段的第8个块
确实在内存里
这时oracle就直接在内存里面把这个块给读了
不需要再到磁盘上去读


这时候我们发现oracle在读这个块的时候命中了一次


oracle接着读13行的时候发现还是这个块
在内存里又找到了


发生三次读其中一次物理读两次逻辑读
结果赚了
这就是以前我们讲过的命中率的概念


物理io就是块block从磁盘到内存叫物理io
逻辑io就是所读的数据块直接在内存里面
物理io叫磁盘读
逻辑io叫内存读


buffercache里面也有命中率的概念
而且buffercache里的命中率比sharedpool的命中率更重要
因为给buffercache来讲只要是没有命中一定发生物理读
只要是物理读一定有寻道,就一定有磁盘的旋转
就一定产生相对比较差的性能
这就是讲的buffercache的其中一个作用
缓存dbf从而减少物理io


3)buffercache第二个意义


在buffercache里面除了缓存block以外
第二个意义构造cr块


cr块和oracle的隔离级别有关系


隔离级别(isolation level),是并发控制的整体解决方案,是指事务与事务之间的隔离程度,
来解决事务并发效率和异常控制。
锁是数据库并发控制的内部机制,是基础。
对用户来说,只有当事务隔离级别无法解决一些并发问题和需求时,才有必要在语句中手动设置锁。


在多事务环境下,事务隔离对产生cr块有影响


一个会话
这里暂且认为是一个sqlplus登上来了


提交的概念:
比如做了insert语句
这时候insert修改并没有真真实实的保存起来
只有commit以后
才能实实在在的保存起来


oracle有这么一个原则
一个会话所修改的数据在没有commit以前
别的用户是看不到的


前面的课程中我们已经在t2里面插入了两行数据
并且已经提交了
现在另外再起一个会话执行
SQL> select * from t2;

        ID NAME
---------- --------------------
         1 xkj
         2 jiagulun
原会话中的执行结果看到了,因为它已经提交了


也就是说oracle数据库里面
我可以直接读已经提交的数据


假设另外做一件事情
现有两个会话
会话1中:
SQL> delete from t2 where id=1;

1 row deleted.
删了一行,t2表中有两行删了一行
但是没有提交


会话2再来读这个块的时候
会话1已经把这一行删了
这个行在这个块里没有了
会话2要读这个块
因为会话1还没有提交
就是对块的修改还没有提交
会话2不能直接读这个块


这样会话2会在内存里面在buffercache里面再单独的申请一个新块
将数据填进新块
第二行因为没有修改直接填回来
第一行因为已经修改了但是还没有提交
这时这个会话2会把第一行找出来
把第一行修改前数据找回来填到新构造块里面去


第一行尽然在原块里面被删了
会话2去哪里找这一行呢


oracle数据库里面
有这么一堆dbf我们叫回滚数据叫undo
叫undo空间
这些文件只做一件事情
会话1删了一行数据
这时候被删的数据就会进到undo里面去
也就是原来这个块里面被修改的修改前的数据被写到undo里面去


会话2读这个块的时候
发现这个块里有一个行被删了但删还没有提交
它就会到undo里面去找到这一行
把它写到新构造块里面去
这时对会话2来讲
就放弃对原块的读
直接读新构造块


再看实例
会话1已经把t2的一行删了
在会话2中读:
SQL> select * from t2;

        ID NAME
---------- --------------------
         1 xkj
         2 jiagulun
我们发现还是两行

会话2读原块的时候t2只剩一行了,为什么读出来两行呢
是因为会话2读这个块的时候发现这个块里面的第二行没有被改变直接写过来
第一行被改变了而且还没有被提交
于是就去undo里面把已经改变没有提交的改变前的数据写到这个块里面去
然后读出来


这个新块就叫cr块


oracle数据库在改变一个块以前
它会把改变前的数据写到undo里面去
undo的作用在删完一行数据可以后悔
认为删错了可以强制执行一个命令rollback


会话1中
先读一下当前数据
SQL> select * from t2;

        ID NAME
---------- --------------------
         2 jiagulun


然后回滚


SQL> rollback;

Rollback complete.

rollback,就是把上次提交以后
到目前数据的改变恢复出来


当前在会话1中就是把
delete的操作取消了
原理是在undo里面把修改前的数据给复制覆盖回来


再去读
会话1中:
SQL> select * from t2;

        ID NAME
---------- --------------------
         1 xkj
         2 jiagulun
会话2中:
SQL> select * from t2;

        ID NAME
---------- --------------------
         1 xkj
         2 jiagulun
结果是两行。


目前为止undo有几个作用
第一个
可以对没有提交的操作回滚
第二个作用
构造cr块
构造cr块需要的空间在buffercache里面


buffercache有两个作用
一个缓存block,减少物理io,这里面有命中率的概念
一个构造cr块


oracle可以办到:
我做了一些操作我只要没提交我就可以回滚,就是可以后悔,取消前面的修改
只要未提交,别的会话就看不见修改,看不见怎么实现呢,就是通过cr块


这节讲了block、buffer包括undo等等这些概念


2016年9月10日
    文字:韵筝