深入 Oracle 基础压缩(Basic Compression)

Tags: database oracle

前一篇文章中我们讨论了一些压缩方式,现在是时候讨论一下 Oracle 如何进行 BASIC 压缩了。它实际上并不是真正的压缩,不过它确实可以节省哪些不会被使用的数据所占的空间。让我们来看看 Oracle 如何减少你数据所占用的空间吧。

Oracle 会通过查找并去重常见的字符串,并标记然后在字符串中使用标记令牌来缩减行的长度。那么,这到底是什么意思呢?通过实际的例子或许能得到一些帮助,首先通过下面命令来创建一张表并初始化数据:

--
-- Create and populate the table
--
create table comptst(
    tstcol1 varchar2(4),
    tstcol2 varchar2(6),
    tstcol3 number(8,2),
    tstcol4 varchar2(10));

insert into comptst
values ('ZZXZ', 'bbddff', 199.44, 'PENDING');

insert into comptst
values ('ZZXZ', 'ghijkl', 43.08, 'PENDING');

insert into comptst
values ('ZZXZ', 'bbddff', 881.02, 'PENDING');

insert into comptst
values ('ZZXZ', 'bbddff', 54.97, 'PENDING');

commit;

insert into comptst select * From comptst;
insert into comptst select * From comptst;
insert into comptst select * From comptst;
insert into comptst select * From comptst;
insert into comptst select * From comptst;
insert into comptst select * From comptst;
insert into comptst select * From comptst;
insert into comptst select * From comptst;
insert into comptst select * From comptst;
insert into comptst select * From comptst;
insert into comptst select * From comptst;
insert into comptst select * From comptst;
insert into comptst select * From comptst;
insert into comptst select * From comptst;
insert into comptst select * From comptst;
insert into comptst select * From comptst;
insert into comptst select * From comptst;
insert into comptst select * From comptst;
insert into comptst select * From comptst;
insert into comptst select * From comptst;
insert into comptst select * From comptst;
insert into comptst select * From comptst;
insert into comptst select * From comptst;
insert into comptst select * From comptst;
insert into comptst select * From comptst;

commit;
                --
-- Compress the table with BASIC compression
--
alter table comptst compress basic;
alter table comptst move;

_[在数据插入后需要两个步骤来压缩这张表,首先需要设置压缩等级,以及执行 table move 来执行压缩动作。如果建表时指定了压缩方式那么插入数据后会直接压缩无需执行额外的动作]_从第四行 INSERT 语句开始,后面的语句会重复插入已有数据,因此表中有很多重复数据。由于 Oracle 会对行进行去重来达到压缩效果,因此块转储(block dump)中应该有大量的数据表明这个行为。没错,转储数据的第一行是:

perm_9ir2[4]={ 0 2 3 1 }

Oracle 为每一个数据块创建了标记表(token table),这为块中每个多次出现字符串数据提供了一个参考。另外,Oracle 可以在标记表中重新排列列值,这样多个列值可以变为一个单独的标记,同样是一个单独的引用。上面的信息表示当前块的标记表中哪些列值和表的映射关系,这里 第零列映射到表第零列上的数据,第一列映射表第二列上的数据,第二列映射表第三列上的数据,第四列映射表第一列上的数据。让我们看看去重后的已插入数据:

('ZZXZ', 'bbddff', 199.44, 'PENDING');
('ZZXZ', 'ghijkl', 43.08, 'PENDING');
('ZZXZ', 'bbddff', 881.02, 'PENDING');
('ZZXZ', 'bbddff', 54.97, 'PENDING');

由于这些数据行是重复的因此在每个数据块中每列数据都是潜在的压缩标记。有两个值在每一列中都存在:'ZZXZ' 和 'PENDING',因此可预期的是在这两个值能够在每个已压缩的数据行中找到。就如前面提到的那样,Oracle 在每个块中构建标记表(token table),所以当前块中应该有两张表:首先,从偏移位置 0 开始是一个包含 7 行的标记表;然后从偏移位置 7 开始是 721 行实际的表数据。

0x24:pti[0] nrow=7      offs=0
0x28:pti[1] nrow=721    offs=7

Oracle 有一个这种压缩实现的线索,并且可以通过同一个标记表来创建包含数值和标记(token)的标记(token),来减少行长度以及更多。上面的例子不能够演示这点,但是要知道这是可能的。现在让我们通过这个块的第一行来看看数据表:

tab 1, row 0, @0x1f31
tl: 5 fb: --H-FL-- lb: 0x0  cc: 4
col  0: [ 4]  5a 5a 58 5a
col  1: [ 7]  50 45 4e 44 49 4e 47
col  2: [ 6]  62 62 64 64 66 66
col  3: [ 3]  c1 37 62
bindmp: 2c 00 01 04 02

每一列的实际的列长度在方括号([])中给出,总长度是这些值的和另增加 7 个字节,其中:4 个字节用来表示列长度,1 个字节是锁定字节,1 个字节是标志位字节,1个字节是列总数。根据前面的描述总长度是 24 个字节,块转储信息在 'tl' 部分提供了不同的总长度 5。在行转储信息的底部 'bindump' 标签标注的位置(行信息的二进制转储)显示实际的内容为 5 个字节。正如预期的这里有锁定字节(lock byte,0x2c),在这个位置(0x01)的列个数,以及两个字节用代表标记(token),显示这个标记有 4 列,以及标记表中的参考行是行 2,让我们看看表 0 和行 2 的信息:

tab 0, row 2, @0x1f5c
tl: 10 fb: --H-FL-- lb: 0x0  cc: 4
col  0: [ 4]  5a 5a 58 5a
col  1: [ 7]  50 45 4e 44 49 4e 47
col  2: [ 6]  62 62 64 64 66 66
col  3: [ 3]  c1 37 62
bindmp: 00 b3 04 04 05 06 cb c1 37 62

很像数据航,不过标记的总长度是 10 个字节。bindump 文件的头两个字节指出这个标记在这个块中使用了 179 次,第三个字节指出这个标记有 4 列,后面的两个字节指出最前面的两列仍然是标记:0x04 和 0x05。让我们回到这些标记的标记表:

tab 0, row 4, @0x1f66
tl: 7 fb: --H-FL-- lb: 0x0  cc: 1
col  0: [ 4]  5a 5a 58 5a
bindmp: 00 04 cc 5a 5a 58 5a
tab 0, row 5, @0x1f76
tl: 10 fb: --H-FL-- lb: 0x0  cc: 1
col  0: [ 7]  50 45 4e 44 49 4e 47
bindmp: 00 04 cf 50 45 4e 44 49 4e 47

这些是单列标记,并且每个标记在块中使用了 4 次。这就是 Oracle 如何将行长度从 24 字节减少到 5 字节来减少空间占用的方式。通过块转储我们可以根据 5 个字节重新构建 24 字节的原始数据。

我们可以看到 Oracle 并不是真正执行数据压缩,它是使用标记来替换重复的值,通过这些标记在查询时根据每个块中的行目录(row directory)以及实际的行片段(row pieces)来重建数据,依据检索的字段情况一些不需要访问的数据标记不会被使用。

当然这些重建工作会耗费很多 CPU 时钟,并且对于大表的全表扫描时这会成为一个问题,尤其是遇到"缓存缓冲链(cache buffers chains)"锁时Oracle 执行了较少的一致性检查(consistent gets - examination),这是因为 Oracle 需要在构建过程中占用一段时间数据块。

好的方面是由于数据在更小的块中,因此可以减少物理读而且可以将数据更长时间的放在缓存中。

使用 BASIC 压缩是时间换空间,对于特别大的表或类似情况,压缩可以节省非常多的空间(意思是被压缩的数据更多),查询可能会是 CPU 密集而不是 I/O 密集的操作了。因此在选择压缩方式时这些好处和坏处都需要仔细衡量并谨慎选择。相对于最终用户的满意度而言空间越来越廉价,DBA 对性能的理解和最终用户对性能的理解是不同的,而且实际上来说最终用户的需求才是最高优先级的。

本文链接:http://www.4byte.cn/learning/37776/shen-ru-oracle-ji-chu-ya-suo-basic-compression.html