Oracle调整数据文件大小杂谈

调整数据文件大小的SQL

SQL来源于网络,但是没保存下原作者的连接,在原作者的基础上稍微增加了一些中文的说明
ORACLE在使用过程中数据文件会变得越来越大,有时候删除了冗余的数据仍然不会释放空间,此时需要调整数据文件的大小。

select a.file# as "数据文件id",
       a.name as "数据文件路径",
       a.bytes / 1024 / 1024 as "当前数据文件大小(MB)",
       ceil(HWM * a.block_size) / 1024 / 1024 as "可调整至大小(MB)",
       (a.bytes - HWM * a.block_size) / 1024 / 1024 AS "释放空间大小(MB)",
       'alter database datafile ''' || a.name || ''' resize ' ||
       ceil(ceil(HWM * a.block_size) / 1024 / 1024) || 'M;' as "SQL语句"
  from v$datafile a,
       (SELECT file_id, MAX(block_id + blocks - 1) HWM
          FROM DBA_EXTENTS
         GROUP BY file_id) b
 where a.file# = b.file_id(+)
   And (a.bytes - HWM * a.block_size) > 0
   and rownum < 30
 order by "释放空间大小(MB)" desc

执行后,查询结果中自动显示数据文件的当前大小、可收缩的大小和收缩语句,例如:
在这里插入图片描述
将其中需要调整大小的SQL拷贝出来执行就可以,使用起来非常方便,也非常的安全。

SQL分析

如此好用的SQL,忍不住分析学习了一下。
首先这个SQL查询的是v$datafile表和DBA_EXTENTS这两张表
datafile表中存储的ORACLE数据文件的信息。bytes是数据文件当前的大小,maxBytes是最大大小。上面的SQL通过datafile表计算出了当前数据文件的大小。
那么可收缩的空间是怎么计算的呢?主要是通过DBA_EXTENTS表

DBA_EXTENTS表

首先查看DBA_EXTENTS表的信息

select * from DBA_EXTENTS

在查询结果中,重点关注:

字段说明
OWNER表示拥有者
FILEID数据文件的ID,一个表空间可以有多个数据文件,因此也会有多个FILEID,大文件表空间除外
BLOCK_ID区所在块的ID
BLOCKS块的数量

数据块

其中涉及oracle的基本概念:块。一般说起ORACLE的基本结构就是表空间、段、区、块;
块是ORACLE中存储信息的最小单位,ORACLE的块和操作系统的中的块无关,这也是ORACLE跨平台的需要,可以将ORACLE理解为在操作系统的块上又封装了一层。
ORACLE涉及到数据的增删改查,基本都是以数据块为单位进行的,例如,我们要select一行数据,ORACLE会读取到该行上所有的数据块,再返回数据块上指定的数据行,具体的数据行用ROWID来标识。ORACLE的数据块是有大小的,无论是在windows或者linux中,oracel数据库块的默认大小是8k,可以通过SQL来查看:

select value from v$parameter where name='db_block_size'

查询结果显示8192,就是8K大小。注意越大的数据块并不一定会提升系统的性能,反而可能造成空间的浪费。例如有的人设置了16K大小,那意味着即时只存储了一条数据,也会占据16K的空间,会导致大量的碎片,影响SQL的执行效率。

数据块的增长

在上面的SQL中使用了MAX(block_id + blocks - 1)来计算当前数据文件的实际大小,其中block_id是数据块号,当创建一张表时,10G会立刻分配一个extent区,11g在插入一条数据以后才会分配(这也是为什么11G经常不导出空表,顺带一提)。如果表空间没有特别指定,那么初始分配8个数据块。为什么是8个,原因在就是表空间默认的INITIAL_EXTENT是65536,默认一个块是8192字节,那么初始区就会分配 65536 / 8192个字节。如果数据块是16K,那么初始分配就是65536 / (16 * 1024) =4个块。
当插入数据时,ORACLE会判断当前的数据块是否能容纳,如果空间足够那么新增的数据行会写到当前连续的8个数据块中, block_id取决于当前有连续8个数据空间时的起始ID,例如当前数据文件中第9个块以后有8个连续的数据块,那么此时的block_id就是9。当数据量不断增长,当前8个数据块已经不能容纳新增的数据时,ORACLE会寻找下一个连续的8个块,例如有可能数据块33后面找到了8个连续的数据块,那么此时BLOCK_ID就会变成33。

计算实际大小

所以如果要计算当前数据文件的实际大小,只要知道当前数据文件有多少个数据块,然后乘block_size就可以了,那么自然就是找到最大的那个数据块号 ,加上这个区所拥有的块的数量,减1(但是为什么减1没想明白,囧,不减1影响也不大,无非就是表空间有可能调整的有偏差),这就是当前数据文件所拥有的数据块的数量然后乘以block_size,就是当前数据文件实际的大小了。

行链接和行迁移

SQL是整明白了,然后对于数据块的学习和理解,又把以前的几个知识点串起来了。
当新增一行数据时,如果一个数据块(注意是一个,不是8个)已经容纳不下这行数据了,就会把这行数据存储在几个连续的快中,同时数据块的数据区域存储下一个块的地址。这就是行链接;
当修改一行数据时,如果一个数据块已经容纳不下该行数据了,就会把这行数据迁移到其他的数据块中,这叫行迁移。
当一个数据块被频繁的读取、修改,就出现了“热块”
行链接、行迁移、热块都会影响SQL的执行效率,所以就想明白了以前的一些说法:

避免一行数据包含过多的数据。

因为超过了数据块的大小,就会发生行迁移和行链接,从而导致IO性能下降而影响SQL执行效率,此时在AWR报告中可以看到IO消耗明显偏高

高水位线HWM

在ORACLE中创建一张表时,会为这个表分配一个段,这个段里会创建一个初始区,在段中第一个区的第一个块就称为段头(SEGMENT HEADE),HWM存储在段头中,一个形象的比喻是可以把段表理解成一个杯子,当数据保存时,这个杯子里的水位线就上升,一直到上升到一个阈(yu,四声)值的时候,ORACLE就会认为当前数据块空间不足,然后把数据劈开保存下一个数据块。
当插入数据时,HWM就向“杯子”的顶端移动,当删除数据时,数据块被清空,但是HWM仍然在原地,在原地,在原地~!因此HWM上面都是可用空间,HWM下面都是已经存在的数据块和保留空间。那么就有一个有意思的问题。当我们发送一个SELECT的语句的时候,有可能出现HWM下面什么都没有,但是仍然要进行扫描,因为此时ORACLE并不知道数据块的具体位置,所以要扫描一个段中所有的块,段是什么?段其实就是表,扫描所有的块通俗的讲就是全表扫描。
那么怎么避免?只要让ORACLE知道要查找的数据块在什么位置就好了,这就是索引。
ROWID是ORACLE获取数据块最快的方法,为什么索引快?因为索引中储存的就是ROWID(这么讲其实不够准确,但是方便理解)。
扫描100万个数据块找到其中10个数据块,不如通过索引直接告诉ORACLE这10个数据块的ROWID,这就是索引对效率的提升
所以我们平常工作的时候经常说“我擦,SQL慢是因为全表扫描了要建索引”,但是从ORACLE的原理上其实我们应该说:我擦,SQL慢是因为扫描了段表中所有的数据块导致数据库的IO升高因此要建索引让ORACLE直接获取到对应的ROWID以降低扫描所带来的IO消耗。
这才是专业的说法,不怕被其他人锤死的同学可以尝试一下这种表达方法
想想AWR报告,那些IO消耗高的SQL,是不是大部分原因都是全表扫描和索引不合理?

热块

被频繁读取和修改的数据块,称之为热块,热块也是导致IO性能的原因之一。
为什么会出现热块,是因为数据快中存储了太多的数据,
怎么解决?那就是避免一个数据块中存储过多的数据,
网上有说加大表或者索引的PCTFREE,让一个数据块中存储更少的数据,但是这样会导致数据存储在更多的数据块中,意味着ORACLE读取一行数据时要扫描更多的数据块,所以我觉得这个解决方法需要针对具体的场景。
网上有说设置block_size更小的表,但是这样的话一个生产环境上不同的表有不同的block_size,难道不会增加运维的成本吗?
有说使用Keep Buffer Pool,我觉得这个是比较靠谱的方法,用ORACLE的机制来解决ORACLE的问题。但是Keep Buffer Pool要比“大多数”表的数据行大小更大一些,不然就会导致Keep Buffer Pool中的数据被频繁的清理,在AWR报告中就体现为逻辑读超高,启用的方法执行:alter system set db_keep_cache_size=50m;但是谨慎,我对这个参数并没有太多的研究,在这里写出来仅仅是为了抛砖引玉,给大家提供更多的思路。

结束语

一个调整数据文件大小的SQL,细细分析一下却能学到不少的知识点,就像沙滩上的点点贝壳,以前为了尽快解决问题而不求甚解,但是往往就陷入了知其然但是不知其所以然的窘境。碰到网上没有的问题就变得一筹莫展甚至束手无策。所以还是要更多的了解技术上的实现细节:我们不提倡重复的造轮子,但是总得知道轮子是怎么造的吧,不然真碰到问题了连排查的思路都没有。

  • 2
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
可以帮你对oracle整体知识的掌握 基本数据类型: CHAR(n) 定长字符串 最大长度为2000 VARCHAR2(n) 变长字符串 最大长度为4000字节 没有默认长度必须指定 NCHAR(n) 用于存储定长为n的Unicode字符最大长度为2000字节 NVARCHAR2(n) 用于存储变长为n的Unicode字符串 最大长度为4000字节 LONG 存储最大长度为2GB的变长字符数据一个表中只限有一列有LONG 不能为主键,唯一约束 要现实使用中要将LONG转为CLOB NUMBER(p,s) p为有效数字的个数 , s为小数位数 NUMBER(p) p为有效数字的整数 NUMBER 表示精度为38的浮点数 DATE 用于存储日期和时间格式的数据,DD-MON-YY TIMESTAMP 时间戳类型。可以包含小数秒 TIMESTAMP WITH TIME ZONE 与前一种类似但是加了时区的支持 LOB数据类型存储非结构化数据,比如二进制文件,图形文件或其他外部文件。LOB可以存储到4GB。数据可以存储在数据库中也可以存储到外部数据文件中 BLOB 二进制大对象,可以是图像,音频文件以及视频文件 CLOB 存储字符存储字符大对象 BFILE:存储外部二进制文件,文件的大小由操作系统决定。 ROWID 数据库中的每一行都有地址,用于记录每一条存储表中的每一条数据记录 RAW:这是一种变长的二进制数据类型,采用这种数据类型存储的数据不会发生字符集的转换,可以把它看做是由数据库存储的信息的二进制字节串。最多可以存储2000字节 LONG RAW 能存储2GB的二进制信息,建议所有开发中都使用CLOB

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

低音钢琴

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值