探究 Oracle 高水位对数据库性能影响

af558e767a8df8473328c9d0f1550755.png
陈龙

云和恩墨西区工程师

本文整理来自上周四晚云和恩墨大讲堂陈龙的分享:探究 Oracle 高水位对数据库性能影响。

“之所以分享这个学习内容,是因为在我曾经经历的一些优化项目中,遇到由于高水位导致 SQL访问性能下降的问题,这促使我想更深入的去理解:什么是高水位线? 高水位到底会带来哪些影响? 应该怎样更好的解决高水位带来的问题?”

在开始深入分析之前,让我们先来了解一下高水位线 HWM。

一. HWM 的基本原理 (概念)

在 Oracle 中,高水位线(High-warter mark, HWM)被用来形容数据块的使用位置,即说明那些块是已经被使用的,那些没有。在创建一个新的表(段)时,Oracle 就会为这个对象分配第一个区,这个区的第一个块在创建时就会被分配存储一些表头信息。

高水位的管理机制在 MSSM 和 ASSM 中不同,下面分别介绍这两种管理机制:

1手动段空间管理(MSSM)

在以往的手动段空间管理中(MSSM),高水位标记 HWM,一个段分成三部分,header block,used block(row data),unusedblock,其中 used block 和 unused block 之间的分界线就是高水位标记 HWM,当进行全表扫描的时候,会扫描到 HWM 下的所有数据块,即使 used block 中很多数据被删除了,全表扫描还是以HWM为准。当 insert 时,freelist 中如果没有发现空闲块,就会在 HWM 上找寻未用块,同时 HWM 会向上移动。但是需要注意,HWM 只会向上移动,不会自动收缩,即使 delete 大量数据,导致 HWM下有很多空闲块。如下图:

7e7e6ad181dda225a09ea357053cea2c.png

第一个区的第一个块就称为段头块 (SEGMENT HEADE),段头中储存了一些信息,其中 HWM 的信息就存储在此。此时,因为第一个区的第一块用于存储段头的一些信息,虽然没有存储任何实际的记录,但也算是被使用,此时 HWM 是位于第2个块。当我们不断插入数据到 PM_USER 后,第1个块已经放不下后面新插入的数据,此时,ORACLE 将使用高水位之上的块用于存储新增数据。

c60c9b26cb143e989b5f219ac7c138f9.png

在向表里插入数据后,HWM 本身也向上移。也就是说,当我们不断插入数据时,HWM 会往不断上移。这样在 HWM 之下的块,就表示使用过的块,HWM 之上的就表示已分配但从未使用过的块。

15afc822e830609cba6eb6d28e740a75.png

当对表进行导出、导入,或者 move 操作后,此时相当于新表插入数据, HWM 就会重新回到使用块的真实水平上。

7a86d8dd2a915819e5c9db4c4a8629d2.png

2自动段管理(ASSM)

在自动段管理(ASSM)中,利用位图来代替空闲列表,当会话向表插入数据时,数据库只格式一个单独的位图块,而不是像 MSSM 中那样,会预先格式化一组块。在ASSM表空间中,除了一个 HWM 外,还有一个低 HWM。在 MSSM 中,HWM 推进时,所有的块都会格式化并立即生效,这样 Oracle 就可以安全的读取这些块。但是对于 ASSM,当 HWM推进时,Oracle 并不会立即格式所有的块,只是在第一次使用的时候才会对这些块进行格式化。也就是说,在第一次使用的的时候,即进行 insert 操作时,数据会插入到块中的任意水位线,位于低水位线(LHMW)和高水位线(HHMW)之间。因此在这个区域的许多块就不会被格式化。

在一个 ASSM 段中的每个数据块可能为这些状态:

▶ 在 HWM 之上

这些块都是没有格式化,且没有被使用

▶ 在 HWM 之下

这些块会处于这些状态之一:

  • 已经分配,但是没有被格式化且没有被使用

  • 已经格式化且包含数据

  • 已经格式化,但是已经删除数据,块为空

下面来了解整个过程:

 52c7318c9180595d79d9a1dc786f6a42.png

如图所示,在没进行数据插入前,段中的所有数据块都是没有格式化的且没有被使用。

假设这个时候某会话将数据插入时,数据库会把数据写到任何可以利用的空间块中。如下图所示:

735114f834f2a3729cb6e76d234932f2.png

数据库也可能会选择 HWM 和低 HWM 之间的任何数据块,或者低 HWM 之下的任何可用的空间的块。注意低 HWM 位置,因为 HWM 之下的块只有在被使用的时候,才进行格式化,所以当存在数据扫描时,特别是全表扫描,Oracle 会读取到低 HWM 的位置。如下图所示:

b82e4384cbbbd43a1487ab268e7c8765.png

如果某条会话将数据插入到某表中,但是在当前的 HWM 之下没有足够的可用空间,那么这个时候 Oracle 会推进 HWM,重新分配一组新的没有格式化的块。当 HWM 与低 HWM 之间的位置被填满时,HWM 会继续往前推进,而低 HWM 会相应的推进到旧的 HWM 位置中。以此类推,当数据库不断的插入数据,HWM 会持续往前推移,而低 HWM 会尾随其后,除非重建、或缩小该对象等,否则 HWM 从不往回退。

b3076bffb863b5e9cb1cc9a1d03ab8f9.png

二. HWM 演示过程

原理描述完了,那么现在我们来测试一下,验证之前我们所描述的部分。

我们先从 MSSM 管理的方式开始:

7663bfe3099e6ef84a5ec71d84a0cf62.png

新建一个表,然后存储分配较多的对象,

17a982c12f88c4e252d1ef85491df14e.png

插入较多的数据,

90918c7f1dd84fc0806a24cb587e2c8b.png
现在来查询下全表扫描所花时间,

f7f1bc8ae24ed744e2d43a8de9256d2d.png

从执行计划来看,该查询走得时全表扫描,期间产生6278个物理读,30028个逻辑读。现在来分析该表,

fc9e1064c8e1e71dd80cc7794684cea0.png

查看统计信息,

0c93e12670e455486e42fbc67e2a0774.png

从该表的统计信息来看,该表共有15384个块,其中没有用到的块有0个,共有1003089行。

现在来测试下 delete 对 hwm 的影响,

8b9a0f3b9d3efea3c6488e7f101dd5eb.png

很奇怪的现象,这个表已经没有数据了,为什么查询时间还是和以前的差不多,其中逻辑读是 30532,比之前还多了。

现在再对该表进行分析,

cb91843a93ae3baa1d8fe50bfe65094d.png

从统计信息来看,该表使用的块还是15384个块,但是空余块依然是0个,按道理来说,在delete 后,应该会有空余的块才对。其中,这里可以看出行数已经变成了0。

从这里就可以验证了之前的说法,即 HWM 在插入数据时,当现有空间不足而进行空间的扩展时会向上移,但删除数据时不会往下移。这就会造成 ORACLE 的全表扫描是读取 HWM下的所有 BLOCK,也就是说,不管 HWM 下的 BLOCK 现在实际有没有存放数据,ORACLE 都会一一读取,这样,在 DELETE 表后,ORACLE 读了大量的空块,耗去了大量的时间。

测试环境没有 show_space 这个过程,现在声明它:

SQL>createorreplaceprocedure show_space

 2 ( p_segname_1 invarchar2,

 3 p_space invarchar2default'MANUAL',

 4 p_type_1 invarchar2default'TABLE',

 5 p_analyzed invarchar2default'N',

 6 p_owner_1 invarchar2defaultuser)

 7 as

 8 p_segname varchar2(100);

 9 p_type varchar2(10);

 10 p_owner varchar2(30);

 11 l_unformatted_blocks number;

 12 l_unformatted_bytes number;

 13 l_fs1_blocks number;

 14 l_fs1_bytes number;

 15 l_fs2_blocks number;

 16 l_fs2_bytes number;

 17 l_fs3_blocks number;

 18 l_fs3_bytes number;

 19 l_fs4_blocks number;

 20 l_fs4_bytes number;

 21 l_full_blocks number;

 22 l_full_bytes number;

 23 l_free_blks number;

 24 l_total_blocks number;

 25 l_total_bytes number;

 26 l_unused_blocks number;

 27 l_unused_bytes number;

 28 l_LastUsedExtFileId number;

 29 l_LastUsedExtBlockId number;

 30 l_LAST_USED_BLOCK number;

 31 procedure p( p_label invarchar2, p_num innumber)

 32 is

 33 begin

 34 dbms_output.put_line(rpad(p_label,40,'.')||

 35 p_num );

 36 end;

 37 begin

 38 p_segname :=upper(p_segname_1);

 39 p_owner :=upper(p_owner_1);

 40 p_type := p_type_1;

 41 

 42 if(p_type_1 ='INDEX'or p_type_1 ='index')then 

 43 p_type :='INDEX';

 44 endif;

 45 if(p_type_1 ='TABLE'or p_type_1 ='table')then 

 46 p_type :='TABLE';

 47 endif;

 48 if(p_type_1 ='CLUSTER'or p_type_1 ='cluster')then 

 49 p_type :='CLUSTER';

 50 endif;

 51 

 52 dbms_space.unused_space

 53 ( segment_owner => p_owner,

 54 segment_name => p_segname,

 55 segment_type => p_type,

 56 total_blocks => l_total_blocks,

 57 total_bytes => l_total_bytes,

 58 unused_blocks => l_unused_blocks,

 59 unused_bytes => l_unused_bytes,

 60 LAST_USED_EXTENT_FILE_ID => l_LastUsedExtFileId,

 61 LAST_USED_EXTENT_BLOCK_ID => l_LastUsedExtBlockId,

 62 LAST_USED_BLOCK => l_LAST_USED_BLOCK );

 63 

 64 if p_space ='MANUAL'or(p_space <>'auto'and p_space <>'AUTO')then

 65 dbms_space.free_blocks

 66 ( segment_owner => p_owner,

 67 segment_name => p_segname,

 68 segment_type => p_type,

 69 freelist_group_id =>0,

 70 free_blks =>l_free_blks );

 71 

 72 p('Free Blocks', l_free_blks );

 73 endif;

 74 

 75 p('Total Blocks', l_total_blocks );

 76 p('Total Bytes', l_total_bytes );

 77 p('Unused Blocks', l_unused_blocks );

 78 p('Unused Bytes', l_unused_bytes );

 79 p('Last Used Ext FileId', l_LastUsedExtFileId );

 80 p('Last Used Ext BlockId', l_LastUsedExtBlockId );

 81 p('Last Used Block', l_LAST_USED_BLOCK );

 82 

 83 

 84 /*IFthe segment is analyzed */

 85 if p_analyzed ='Y'then

 86 dbms_space.space_usage(segment_owner => p_owner ,

 87 segment_name => p_segname ,

 88 segment_type => p_type ,

 89 unformatted_blocks => l_unformatted_blocks ,

 90 unformatted_bytes => l_unformatted_bytes,

 91 fs1_blocks => l_fs1_blocks,

 92 fs1_bytes =>l_fs1_bytes ,

 93 fs2_blocks => l_fs2_blocks,

 94 fs2_bytes =>l_fs2_bytes,

 95 fs3_blocks => l_fs3_blocks ,

 96 fs3_bytes =>l_fs3_bytes,

 97 fs4_blocks => l_fs4_blocks,

 98 fs4_bytes =>l_fs4_bytes,

 99 full_blocks => l_full_blocks,

100 full_bytes => l_full_bytes);

101 dbms_output.put_line(rpad(' ',50,'*'));

102 dbms_output.put_line('Thesegment is analyzed');

103 p('0% -- 25% free space blocks', l_fs1_blocks);

104 p('0% -- 25% free space bytes', l_fs1_bytes);

105 p('25% -- 50% free space blocks', l_fs2_blocks);

106 p('25% -- 50% free space bytes', l_fs2_bytes);

107 p('50% -- 75% free space blocks', l_fs3_blocks);

108 p('50% -- 75% free space bytes', l_fs3_bytes);

109 p('75% -- 100% free space blocks', l_fs4_blocks);

110 p('75% -- 100% free space bytes', l_fs4_bytes);

111 p('Unused Blocks', l_unformatted_blocks );

112 p('Unused Bytes', l_unformatted_bytes );

113 p('Total Blocks', l_full_blocks);

114 p('Total bytes', l_full_bytes);

115 endif;

116 end;

117 /

SQL>EXEC SHOW_SPACE('TEST_TAB','CHENLONG');

Free Blocks.............................15384

Total Blocks............................16384

Total Bytes.............................134217728

Unused Blocks...........................999

Unused Bytes............................8183808

Last Used Ext FileId....................12

Last Used Ext BlockId...................15369

Last Used Block.........................25

PL/SQLprocedure successfully completed.

Elapsed:00:00:00.14

这里共有 16384 个块,空余 15383 块,其中没有使用的块的有 999 个块,LAST USED BLOCK 表示在最后一个使用的 EXTENT 中使用的 BLOCK.

这里有一个计算 HWM 位置的公式:

fe8440d50786b29e7189e60612d057d3.png

根据这个公式那么 HWM 位置:

a5d0c1e72c213deee1fc573a2e4e9c95.png

那么这个就是 HWM 所有的 BLOCK 编号。

HWM 所在的块:

39b4eb74d9b9efbcb656e6387dab0669.png

也就是说,HWM 在第 15385 个块,其 BLOCKID 是15343.

9331d458757560b6dad03e9e7487c60b.png

在使用 ALTER TABLE TEST_TAB DEALLOCATE UNUSED 后,UnusedBlocks 变成了7个,其余的和之前的没什么变化。

b7779e57b433ac7f0cfebb1adc4fb747.png

在执行 ALTERTABLE TEST_TAB MOVE 后,有了很大的变化。

那么 HWM 位置 :

eb7f778abdeab3dbd34b082272394d8b.png

那么这个就是 HWM 所有的 BLOCK 编号。

HWM 所在的块:

1a77f540c119b788d5a76b693b1e8361.png

这里比较奇怪的时,没有数据,但是 Unused Blocks 是7,

查询查询该表,

389f1fbf29e6b4c25ce97b693d26d160.png

执行时间就变成了1毫秒,逻辑读变成了3,物理读变成了0个。

现在,插入一些数据,

cd429fcbf42e9e3429b6f12bcd47f860.png

分析表的统计信息,

06959e30d778ef79367499fa0b3ffd26.png

从统计信息来看当前块变成了1,总行数为1行。

f78f0df7cbeb8a73c41dd2212125370c.png
从中我们也可以发现,分析表和 SHOW_SPACE 显示的数据有点不一致,其实这两个都是准的,只不过计算的方法有点不同。

现在,再来试试其他方法,在测试前还是插入数据。

0ebbd1161b5a86b06f5cff562d4ff825.png

分析表的统计信息,

e57695c73345548519ff4abeeba88937.png

TRUNCATE 后,块数变成了0,行数变成0.

实际上除了 MOVE 操作还有 SHRINK SPACE,都能够是高水位线重置。

最后,再来看看在创建表时,会默认分配多少个块。

e1e5e399e17ef8c79a1867e3eb85ff4d.png

INITIAL 等于 65536,即等于 64kb,一个标准块是 8K,正好是8个 block。

PCTFREE 代表着设定为 10 ,意思就是说,当 BLOCK 使用到达 90% 的时候,就不可以在使用了,这个 BLOCK 应该从 FREELIST 列表中移除 (un-link) .

a4bd91be94a05af38d4711ea6c9297cb.png

以上是 MSSM 管理下 HWM 的分析,在分析中数据不是很直观的看出 HWM 的移动,下面再举一个 ASSM 管理下的 HWM 移动例子:

e99664cfadc0c4633edd1f7b12913465.png

插入数据:

0b2abae5b89745cc9eedf7140902d6c4.png

然后对该数据文件段头进行 dump 分析:

dcae035fa34d7dfd83e42cbe98b71e05.png

可以看到此时的 LHWM 的 block 位置是在 0x01c00080,同 HHWM 位置,

truncate 该表: 

SQL>truncatetable test_hwm;

Table truncated.


重新 dump 文件.

eaba35c1de562d97d707787177516b21.png

此时 HHWM 和 LHWM 的 block 位置并没有发生改变。

再插入更多行。

ffb7ff8f4d19221c95a5ea0e7db37ffd.png

在重新插入了数据后,HHWM 与 LHWM 位置发生了改变,HHWM 往前推移。

下面测试下利用 append 方法插入数据会怎么样:

ec7cc6fd88927f9324bbf78c7cfe096c.png

查看 dump 文件:

23be6eb0854d586c3c448936bc200502.png

可以看出 LHWM 位置并没有发生改变,而 HHWM 往前推移了。以 append 方式批量插入的记录,其存储位置在 hwm 之上,即使 hwm 之下存在空闲块也不能使用。

用 insert /*+append*/ 后,数据的 undo 和 redo 没有生成。因为 HWM 在移动的过程中,这些 block 是不能被其他 process 使用的,那么意味着,只要记录下该次 direct insert 所涉及到的空间的 redo 和 undo  ,在失败回滚的时候,只需要把这些空间修改为原来的状态就可以,而不用逐个记录去 delete。

三. HWM 的利弊

在我们的系统中,如果经常对一个存储对象进行删除和更新,就会在它分配的空间内形成一定的零散空间,这些零散的空间将会影响到 SQL 的访问性能。Oracle 数据库通过跟踪段中的状态来管理空间,于是引进了高水位标记(high water mark)。这样可以区别出那些数据块是没有格式化和未使用的,也就是说当请求新的空闲块,并且现有空闲列表中的块不能满足要求时,HWM 指向的块将被标记为已使用,然后 HWM 将移动指向下一个未使用过的块,这样以便于进行数据插入和数据块管理。

然而,频繁的删除并不会使得高水位的“标记”下移,这使得在做全表扫描时,会依然按原来“标记”的位置去尝试查询所有数据(实际上将扫描很多空块),那么访问时间将会被浪费。

四. HWM 的处理

上面讲解了 HWM 的原理和推进过程,如果出现了高水位该怎么办呢?

1Move

所谓 move 操作也就是将一个表的所有数据,在新分配的空间 (extents 中,可以在原表空间,也可以在新的表空间)中插入,当单个 extents 不够时,继续分配更多的 extents 放入数据,知道将所有的数据放入后,在最后的数据所在的块后标志 HWM。从上面的描述可以看出,move 相当于创建新的段,把数据插入,然后将原来的空间释放,而新段的 HWM自然就随着新插入数据后移,这个新建的段就是一个重置 HWM 后的紧凑表。

在使用 move 操作中,有几个注意事项:

  • Rebuild index

    在对表进行 move 操作后,表中的 rowid 发生了改变,这样导致索引无法定位到原来表中的数据,从而触发了索引失效,所以需要 alter index index_name rebuild【online】的命令进行重建。

  • 空间分配

    Alter  table move 操作,不管你有没有插入数据,它都会占用一些块,这就像我们在移动文件,很明显,移动的过程中是会占用双倍的空间,因此 ORACLE 必须给它分配必要的空间,这时可能会出现 ORA-01652 告警。

  • exclusive lock

    move 操作相当于将表中所有数据移动,因此在 move 的过程中,oracle 会对表放置了 exclusive lock 锁,此时只能对它进行 select 操作。

当然,move 也有一些其他的好处,例如:

  • move 不但可以重置 HWM,解决松散表带来的 IO 浪费,还可以解决表中的行迁移问题;

  • move 可以将表移动到其他表空间,也可以在原表空移动,这样可以一定程度解决表空间碎片;

  • 如果表空间上有大量表、索引被 drop(或者 truncate),导致表空间前半部分出现大量空闲空间,可以通过 move 将靠后的表移动到前面的空闲空间,从而收缩数据文件。

下面的脚本,可以找出当前数据库中数据文件可以收缩到的最小大小:

SQL>set linesize 200                      

SQL>columnfile_nameformat a50 word_wrapped

SQL>column smallest format999,990 heading "Smallest|Size|Poss

string beginning""Smallest|..." missing terminating quote (").

SQL> column currsizeformat 999,990 heading "Current|Size"

SQL> column savings  format 999,990 heading "Poss.|Savings"

SQL> select file_name,

  2        ceil( (nvl(hwm,1)*&&blksize)/1024/1024 ) smallest,

  3        ceil( blocks*&&blksize/1024/1024) currsize,

  4        ceil( blocks*&&blksize/1024/1024) -

  5        ceil( (nvl(hwm,1)*&&blksize)/1024/1024 ) savings

  6  fromdba_data_files a,

  7      ( select file_id, max(block_id+blocks-1) hwm

  8          from dba_extents

  9         group by file_id ) b

 10 where a.file_id = b.file_id(+)

 11  /

Enter value for blksize:8192

old   2:       ceil( (nvl(hwm,1)*&&blksize)/1024/1024 ) smallest,

new   2:       ceil( (nvl(hwm,1)*8192)/1024/1024 ) smallest,

old   3:       ceil( blocks*&&blksize/1024/1024) currsize,

new   3:       ceil( blocks*8192/1024/1024) currsize,

old   4:       ceil( blocks*&&blksize/1024/1024) -

new   4:       ceil( blocks*8192/1024/1024) -

old   5:       ceil( (nvl(hwm,1)*&&blksize)/1024/1024 ) savings

new   5:       ceil( (nvl(hwm,1)*8192)/1024/1024 ) savings

                                                               Current    Poss.

FILE_NAME                                           SMALLEST     Size  Savings

------------------------------------------------------------ -------- --------

+DATA/prod/datafile/system.256.903827475                 1205    1,210       5

+DATA/prod/datafile/undotbs2.268.903828289                 25       25       0

+DATA/prod/datafile/sysaux.257.903827475                  593      630      37

+DATA/prod/datafile/users.259.903827477                    67      172     105

+DATA/prod/datafile/example.267.903827687                 313      346      33

+DATA/prod/datafile/undotbs1.258.903827477                763      930     167

+DATA/prod/datafile/user_data                               9       50      41

7 rows selected.

SQL> select 'alterdatabase datafile ''' || file_name || ''' resize ' ||

  2        ceil( (nvl(hwm,1)*&&blksize)/1024/1024 )  || 'm;' cmd

  3  fromdba_data_files a,

  4      ( select file_id, max(block_id+blocks-1) hwm

  5          from dba_extents

  6         group by file_id ) b

  7 where a.file_id = b.file_id(+)

  8   and ceil( blocks*&&blksize/1024/1024) -

  9       ceil( (nvl(hwm,1)*&&blksize)/1024/1024 ) > 0

 10  /

old   2:       ceil( (nvl(hwm,1)*&&blksize)/1024/1024 )  || 'm;' cmd

new   2:       ceil( (nvl(hwm,1)*8192)/1024/1024 ) || 'm;' cmd

old   8:  and ceil( blocks*&&blksize/1024/1024) -

new   8:  and ceil( blocks*8192/1024/1024) -

old   9:      ceil( (nvl(hwm,1)*&&blksize)/1024/1024 ) > 0

new   9:      ceil( (nvl(hwm,1)*8192)/1024/1024 ) > 0

CMD

--------------------------------------------------------------------------------

alter database datafile'+DATA/prod/datafile/system.256.903827475' resize 1205m;

alter database datafile'+DATA/prod/datafile/sysaux.257.903827475' resize 594m;

alter database datafile'+DATA/prod/datafile/users.259.903827477' resize 67m;

alter database datafile'+DATA/prod/datafile/example.267.903827687' resize 313m;

alter database datafile'+DATA/prod/datafile/undotbs1.258.903827477' resize 763m;

alter database datafile'+DATA/prod/datafile/user_data' resize 9m;

2shrink

此命令为 Oracle 10g 新增功能,shrink 操作是将原本松散的数据存放结构,通过将表中靠后的行向前面的空闲块迁移,在完成后将完全空闲的区释放,并前置 HWM 到表中最后一个使用块的位置,从而实现松散表重新结构紧凑。注意在执行该指令之前必须启动行移动特性:alter table table_name enable row movement。同时该命令只支持 ASSM 管理的表空间,否则会报ORA-10635: Invalid segment or tablespace type 告警。

3Emp/Imp 或数据泵

使用导入导出的方法也可以达到降低 HWM 的目的,其原理上还是属于重建表的过程。

4DBMS_REDEFINITION

利用在线重定义方法,来对表进行重组,从而移动表的数据。

这个操作要求 table 上有一个主键,并要求预先创建一个带有要求修改的存储参数的 table,以便保存重新组织后的数据。具体的操作过程这里不再过多介绍。

案例分析:

上文讲到了高水位推进的情况,下面分享一个执行大量执行 insert 操作,引起的 enq: HW - contention 等待事件.

4da9d38a5ba427b3de91ba116a0dd13e.png

73111e03b5589508ea80295a2d60b8bd.png

efb681ad4f876504595e408855d81b15.png

从这篇 AWR 报告可知,在过去的2小时里,出现 enq: HW -Contention 等待事件,占用 57%的 DB Time。

一般导致 enq: HW -Contention 等待事件的原因,是由于高水位的推进或者格式化数据块而引起。

如果某条会话要插入一条记录,但是表中数据块已经被格式化了,没有多余的空闲空间容纳下这条记录,这时候 Oracle 会重新格式化另一组数据块。在数据插入前,如果想要移动 HWM 的进程必须要获得 HW 锁,为防止多个进程同时修改 HWM。如果此时产生了争用,则会出现 enq: HW - contention 等待。

我们可以通过 enq: HW -Contention 等待的 event id,来分析该等待事件。

79646ebcc0a06f4d4207eff0a9e65411.png

利用 eventid 1645217925,来查看过去会话信息.

3072777701b974a890a4a674702b5769.png

利用 dbms_utility 包来收集文件号,和所属的块号.

eb7e7437e308e7a7f2b3e07b129f05c6.png
查看段所属的用户.

9b8a4b207e7de32c44bd29a5e1c404a5.png

到这里基本上可以知道 lob 对象上发生了高水位争用,当然也与主机的存储有关,若存储读写较慢,也会产生争用。这里的解决方法可以参考 MOS 文档 [ID 7319120.8]。

上面从原理、实验、案例相结合学习了 HWM 的知识,现在来总结一下。

五. HWM 总结

1. HWM 在插入数据时,当现有空间不足而进行空间的扩展时会向上移,但删除数据时不会往下移,这就好比是水库的水位,当涨水时,水位往上移,当水退出后,留下的水位痕迹还能清晰可见。

2. HWM 的信息存储在段头当中。HWM 本身的信息是储存在段头的,在段空间是手工管理方式时(MSSM),ORACLE 是通过 FREELIST(一个单向链表)来管理段内的空间分配,此时可以利用_bump_highwater_mark_count 隐藏参数调整,_bump_highwater_mark_count 隐藏参数指出了单次高水位上涨的块数,默认值是5。如果表的数据增长快,或者经常看到与 HWM 相关的 enq:HW –Contention 等待,可以考虑设置较大的值。但需要注意的是,该隐含参数将影响整个 DB,过大的设置会带来全表扫描性能的进一步恶化以及空间的浪费,因此设置应谨慎。在段空间是自动管理方式时 (ASSM),ORACLE 是通过 BITMAP 来管理段内的空间分配.

3. ORACLE 的全表扫描是读取高水位标记(HWM)以下的所有块。当用户发出一个全表扫描时,Oracle 访问目标表里的数据,会从该表所占用的第一个区(Extent)的第一个块(Block)开始扫描,一直扫描到该表的高水位线(HWM),即使它什么也没有发现。这样不仅延长了全表扫描的时间,而且影响全表扫描的性能。比如在一个空表执行 count (*)操作,也会花较长的时间才返回0行数据。

4. 在 delete 删除大量数据时,不仅删除时间较长,而且 HWM 保持原位置不动。如果全表的数据确实不再需要,建议使用 truncate 来代替 delete 操作,因为truncate 默认下会对空间进行释放,改变了表中的数据块位置,使得 HWM 重置到段头。其次,truncate 由于不产生回滚,所以在相同的数据量删除的情况下,truncate 会比 delete 快。(注:truncate 不提交回滚,删除数据后可能无法恢复,在删除时要注意)

如何加入云和恩墨大讲堂微信群

搜索盖国强(Eygle)微信号:eeygle,或者扫描下面二维码,备注:云和恩墨大讲堂,即可入群。每周与千人共享免费技术分享,与讲师在线讨论。

a395fe651dd56a7d47b499bddbae6594.png

云和恩墨

数据驱动,成就未来。整合业界顶尖的技术与合作伙伴资源,围绕数据及相关领域,提供解决方案和专业服务。

IT基础架构

zData一体机 - 分布式存储解决方案

数据架构

Oracle DB2 MySQL NoSQL

专项服务:架构 / 安全 / 高可用 / 容灾 / 优化 / SQL 质量管控

运维服务:运维服务  | 代维服务

人才培养:个人认证 | 企业内训

软件产品:SQL审核 - Z3 | 监控 - Zone | 数据恢复 - ODU

应用架构

应用软件开发:数据建模 | SQL审核和优化 | 中间件服务

业务架构

电子渠道(网络销售)分析系统 | 数据治理

恩墨学院

恩墨学院是云和恩墨(北京)信息技术有限公司旗下的培训事业部,创业数年专注于数据库认证、技能培训,以专业的讲师塑造品牌,以专业的训练保证就业,目前已经发展成为国内数据库领域培训领导品牌。

4b52bce0c6bcf1546a38ab5027c1913e.png

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值