碎片是我们要尽量避免的常见问题。其表现形式多种多样、遍及多种组件,可能导致各种问题。在本文中,我将讨论导致空间浪费的表空间碎片。导致表空间碎片的原因很多,但我从未想到经常执行“shrink table”命令会这么快地导致这种碎片。
在 Oracle 10gR1 之前,表的高水位线 (HWM) 向前移动(由于表中插入新行)之后就无法后退来减小表大小和回收空间,将空间退回给表空间的可用空间。我们从表中删除多行时,HWM 和表大小保持不变,减小表大小的唯一方法是截断表。Oracle 在 10gR1 中引入了一项激动人心的特性“shrink table”。
它是如何工作的?执行 shrink 命令时,Oracle 使用行移动(必须在表上启用)将行从表的最后数块移至表开头处。移动行之后,会发生表锁定,此时 Oracle 将 HWM 向后移动。然后,可以释放 HWM 之后的块,减小表大小。
从下面可以看出,在正常操作中,表中包括“已用块”(蓝色块)和“空块”(橙色块)。将行插入表中,而“已用块”中没有空间时,Oracle 会将 HWM(黑线)移向表末尾,将这些“空块”标记为“已用”。一旦没了可用块,就会再分配一个区。
现在让我们看看 shrink table 是如何工作的。在下图中,我们看到 HWM 位于表的末尾。假设我们删除了许多行,现在表块中有许多地方可容纳新行。shrink table 命令将这些行从表末尾移到靠近表开头部分的空闲位置(第一幅图)。然后,Oracle 可以将 HWM 移到表的最后一行,该行现在不位于表的末尾(第二幅图)。移动了 HWM 之后,HWM 以外的块被视为可用块(第三幅图),然后可将这些块从表中释放,退回给表空间(最后一幅图)。
shrink table 命令只适用于本地管理的、支持自动段空间管理的表空间中的段。
使用本地管理的表空间时,可以通过两种方式配置区分配:
我们来了解一下系统分配的工作原理:
上面的查询将显示各区及其大小。在本例中,它返回 202 行,总结如下:前 16 个区大小为 64KB(在我的数据库中是 8 个 8KB 块)。接下来的 63 个区大小为 1MB。在接下来的 120 区大小为 8MB。剩下来的 3 个区为 64MB。思路很容易理解。
我们看到了 shrink table 的思路,我们明白这是一个非常有用的命令。现在我们将尝试了解有多少空间被退回给表空间。为此,我们将执行以下操作:
现在我们将使用先前用过的查询来获取区分配:
与前面一样,我不在此出贴出完整输出,只贴出摘要:
现在,我们将从这两个表删除数行并收缩表,然后再来看区图。
现在区图显示:
最后需要将 2500 行重新添加到这两个表:
现在区图显示:
问题在于某些我们没有检查或考虑到的东西。要找到问题所在,我们需要在区图中添加区的物理位置。文件中区的位置依 DBA_EXTENTS 表中的 block_id 列而定。block_id 列代表区中第一块的 id,其中 1 是文件开头。
为简单起见,我将使用相关表空间中的一个文件。我们将使用以下查询查看包含区位置的区全图:
此查询的结果将包括相关文件的 file_id、区信息(block_id 和大小)及内容(段名或“free space”)。
我不打算贴出整个结果集。以下是部分 TBS_UNI 结果:
注意 6MB 区之后的可用空间。记住带我们来到此情形的流程。我们填充了表,然后删除了数行,收缩表,再插入更多行。开始时,表的最后一个区大小为 8MB;收缩表后,区缩小到约 6MB,将约 2MB 退回给表空间。插入新行时,Oracle 需要为其分配新区;但似乎这些区不能起始于文件中的任意块。在本例中,新的 8MB 区不能起始于块 14224(该块距文件开头 111.125MB),而是起始于块 14336,该块恰巧距文件开头 112MB。
由于此表空间使用系统分配区管理,其他需要小区的表可以分配此可用空间。但大表不能分配此空间,分配下一区时将留下未用区域。
总结一下这个问题,我们在系统分配区管理表空间收缩表时,会将表末尾的一部分退回给表空间的可用空间。通常,这样做留下的区小于表中通常的最后一个区。表的下一个区并不会总是能够准确地起始于下一块,而是取决于所请求区的大小。如果发生这种情况,各区之间剩下的空间可能只够分配小区,因此通常不能用于同一表或其他大表的区。
现在要讨论两个问题,一个是如何整理表空间碎片,另一个是如何避免碎片。
如果表空间出现碎片,为了消除碎片,我们将不得不丢弃“有问题”的区。问题是通常我们不能这么做,因为表中有太多数据,区太靠近表的开头位置,不能取消分配。我能想到的唯一的解决办法是使用“alter table … move”命令或“insert … select”重新创建表。重新创建表时,会分配一个包含数个新区的新段,释放旧段。丢弃旧段之后,碎片不再存在,但这是一项麻烦的操作,需要计划和停机。
最好的办法是完全避免此问题。这取决于您的具体系统以及您如何使用数据库。以下几点可能值得考虑:
“shrink table”命令在许多情况下都非常有用。但在有些用例中,可能会发生严重的碎片。处理这种碎片很困难,因此最好事先考虑好避免出现碎片。
在本文中,我为您提供了一个我遇到的实际例子,帮助您了解和预防这种碎片问题。希望这对您有所帮助。
Liron 是一位拥有逾 11 年经验的高级 DBA。这些年里,Liron 担任过许多各领域公司的高级顾问并管理 Oracle 咨询团队。他主要擅长高可用性解决方案、性能、备份和恢复及其他基础架构和应用数据库领域。他还是教授 Oracle 课程的知名高级讲师,并在各种活动和论坛发表演讲。Liron 目前是一家领先的以色列 Oracle 咨询公司 Brillix 的专业服务副总裁。