武器装备
oracle数据库管理系统(细说Oracle数据库与操作系统存储管理二三事)

杨建荣,【DBAplus社群】联合发起人。现就职于搜狐畅游,Oracle ACE-A、YEP成员,超7年数据库开发和运维经验,擅长电信数据业务、数据库迁移和性能调优。持Oracle 10G OCP,OCM,MySQL OCP认证,《Oracle DBA工作笔记》作者。

说到存储管理,是操作系统中最重要的资源之一。因为任何程序和数据等都需要占有一定的存储空间,存储管理会直接影响到系统的性能。

关于存储的管理技术,先讨论以下两个部分。

先来点操作系统的知识。

在多道作业系统中,主存中分区的个数是固定不变的,而且每个分区的大小也是固定不变的。如果分区总是大于作业,那么就有很多分区没有充分使用,产生碎片。

在数据库中,shared pool中的free list(bucket)管理和固定分区管理很相似。

在10g,11g中都设置了255个bucket,可以通过trace文件来了解一下。

Bucket 0 size=32

Bucket 2 size=48

Bucket 4 size=64

...

Bucket 251 size=12360

Bucket 253 size=32792

可能对于bucket的大小没有一个直观的感受,可以生成一个图来看看就很清楚了。

随着bucket的增长,对应的chunk大小都在递增,绝大多数的bucket中,chunk的大小都在5k以内。只有很小的一部分bucket的支持的chunk size很大,这个也是Oracle在不断的改进中得到的一个最优值,按照比例来划分,保证每次访问需要的chunk大小都能够合理的分配,尽量减少冗余。

Oracle在早期的版本中也碰到了不少的问题,在10g,11g中都对bucket的数目做了提升(目前都是255个),而且分区的大小也做了调整。这是一个比较均衡的比例,能够保证每次请求的大小都在bucket的范围之内,尽量提高效率。

在存储的管理中,存储的分配和释放都需要根据分区来说明。在固定分区中采用了一个存储分块表(MBT)来维护而存储的区的信息,存储区的信息在操作系统中有一个专有名词叫做数据基,数据基听起来挺抽象,其实理解起来还是蛮简单的。

分区

存储位置

1

xxxxx

2

xxxxx

3

xxxxx

4

xxxxx

5

xxxxx

6

xxxxx

猛一看,上面的方式还是比较简单而且可行的。但是还是固定分区的硬伤,主存利用率不高,对于进入主存中的作业大小我们也没法预知,而且对于MBT表的管理感觉还是不够清晰。如果需要查找哪些分区可用,需要重新分配的时候,就得遍历整个表,遍历了已经使用的分区,这样分配的过程就比较长了。

可变分区的多道管理技术

这样的话,分区个数不固定,分区大小不固定,在Oracle中也有一些相似之处。

  • 比如说对于分区的不固定,在11g中有一个参数deferred_segment_creation,如果我们设定为true,那么在创建之初是不会分配对应的分区的,直到开始插入数据之后,它才会根据插入的数据来创建分区。

    • 如果根据需要动态的创建分区,而且分区的大小也不固定。

      对于可变分区的数据基管理,是采用了两个存储分区表来管理的,已使用分区表(UBT)和空闲分区表(FBT),这样就可以减少存储分配和释放的性能。

      Oracle中早期是采用FET$,UET$ 两个数据字典表来维护分区的信息的。只是在数据基上会有一定的差别。

      细说Oracle数据库与操作系统存储管理二三事nerror="javascript:errorimg.call(this);">

      但是在Oracle的不断改进中,发现这种方式还是存在一定的问题,资源消耗还是比较高的。对于这两种数据字典表的DML操作,会产生较多的递归SQL来间接完成对两个数据字典表的更新,在更新的过程中也会存在事务,存在事务也就会产生一定的undo和redo。最后就是对于相邻空闲空间的合并, 在Oracle中是通过SMON进程来实现的。

      这种方式就是把空闲分区通过链表的形式串起来,形成了一条空闲存储块链。

      在buffer cache中的实现方式也是类似的。当然在Oracle中会采用其它的算法和策略。Oracle中是把buffer按照被使用的先后顺序挂在LRU链表 上,先被使用的buffer放在了链表的后面,后被使用的buffer挂载LRU链表的前面,如果buffer被修改的时候,buffer就会从LRU链 表上取出。这样始终保持LRU链表中都是可用的数据块。

      然后来简单说一下可变分区的存储算法。

      最佳适用算法这种方式就是从所有未分配的分区中挑选一个最接近于作业尺寸且大于或者等于作业大小的分区分配。

    • 最坏适应法把所有未分配的分区中挑选最大的且大于等于作业大小的分区分配。

    • 把所有的分区使用一个位来表示状态,1表示块已经被使用,0表示分区空闲。

    而位图法在表空间管理中也有相似的使用方式。

    本地管理中会在数据文件的头部采用多个位来存放。这个bitmap类似下面的形式。

    1代表分区已被使用,0代表分区还是空闲,当进程需要分区的时候,只要扫描数据文件的头部的bitmap,就可以找到值为0的分区。分配了分区之后把它修 改为1,释放空间就会从1修改为0. 修改数据文件头部的操作速度快且不存在事务,就没有redo,undo,更不会有递归SQL。对于相邻分区的合并来说,两个连续的0就能说明是连续的空闲 分区,所以也不需要再合并相邻的可用分区了。

    分页技术

    分页存储中的基本实现过程,有以下几点:

    1. 用户逻辑地址的分页,用户逻辑地址可以划分为和页架大小相同的部分,叫做页。页号从0开始,依次为0,1,2...

    2. 听起来挺枯燥啊,可以简单举个例子,我们常看的书就是一个很好的例子,书有很多大小,四开,八开,十六开,可以理解为页架,书中的每一页就是我们所说的页,逻辑地址可以这么理解,一本书有很多章节,小结,比如第二章第3页,我们就能够很快找到,这个时候,页号就是2,偏移量就是3,用(p,d)来表示就 是(2,3)

      这一点和Oracle中创建表空间时指定的extent management管理方式很相似,比如我们创建一个表空间test指定分区大小为1M,表空间大小为100M,则语句如下:

      这样我们指定分区大小为1M,如果存储了100M的数据,这样100M就会分为100个分区。如果数据大于分区1M,则可以存储在相应的分区上,不一定连续。

      地址

      页号

      进程1

      1000-1999

      1

      进程2

      3000-3999

      进程3

      4000-4999

      进程2

      5000-5999

      2

      0-999

      2000-3999

      这种方式每次访问一个主存单元都用一次除法得到页号和页内地址就很繁琐,实际上效率要更差。这个时候相比前人也是考虑了很多招数,最后还是使用二进制来搞定,指定页面尺寸是2的幂,这样就会省去很多额外的转换。

      假设页的大小为1KB,计算逻辑地地址为4101的页号,页内地址。

      用0,1来表示就是

      页的大小是1KB=2^10,则在二进制串中,后10位就是对应的页内地址,二进制0101代表的是5,表示页内地址为5

      页号对应的二进制串000100表示页号为4 所以4101对应的逻辑地址表示为(4,5)

      问题来了,地址能够表示了,那使用的时候是怎么转换的呢,首先会把逻辑地址抽取出来,像上面的例子,页号是4,然后根据页号为索引找到该页存放的主存页架号。比如存放的地址为2000-2999,则页架号为2,然后把页架号取代逻辑地址,和右边的页内地址组成了最终的物理地址去访问内存。

      分页中使用的二进制方式处理地址是一种很值得借鉴的方式,可以减少很多额外的开销,和Oracle中的rowid存储方式也很类似。

      分段式存储管理系统中,会为每个段分配一个连续的分区,而进程中的各个段可以离散地移入内存中不同的分区中,说起分段就会联想到分页,我们来聊聊分页与分段的主要区别。

      页是信息的物理单位,分页是为了实现非连续分配,以便解决内存碎片问题,或者说分页是由于系统管理的需要。段是信息的逻辑单位,它含有一组意义相对完整的信息,分段的目的是为了更好地实现共享,满足用户的需要。

    3. 分页的作业地址空间是一维的,分段的地址空间是二维的。

    找一个图来说明。

    从地址的存储情况来说,段和页的存储方式都是类似的,都会包含两部分。分段存储中是段号和段内地址,和分页存储中的页号和页内地址类似。

    由于一个进程由很多段组成,而且各个段可能被分配在主存中的多个不相邻的分区中,为了将进程的逻辑地址转换为物理地址,需要有一个短标来指出进程的某段放在主存中的位置以及段长。

    而段的信息在操作系统层面是通过段表来维护的,数据库层面则是通过数据字典,user_segments,user_extents来维护的,每个表包含的段,每个段包含的区都是很详实的。

    从操作系统层面举个例子就是一个多用户系统,有一个应用程序可能包含的程序段是100K,数据段是40K,按理说需要40K*40+100k*40=1600+4000=5600k

    从这一点上来说,数据库中的同义词就有点分段存储的味道,每个同义词都可以访问源表,相当于共享了数据,同义词占用的存储空间很小,几乎可以忽略。

    当然分页分段方式还在不断的发展中,要不怎么有后续的段页式存储呢,很多时候类比操作系统方面的知识,就会让我们对于很多事物有了全新的认识和了解。

    精选专题(点击蓝色标题可阅读全文)


    顶一下()     踩一下()

热门推荐

发表评论
0评