Firebird 2.1及更高版本中的空间分配
Vlad Khorsun,2010年10月20日
从一开始,Firebird就没有关于如何为数据库文件分配磁盘空间的规则。它只是以不确定的顺序写入新分配的页面(因为页面之间存在依赖关系,以服务于“谨慎写入”策略)。
这种方法非常简单,但有一些缺点:
• 由于写入顺序不确定,可能会出现这样的情况:当必须写入新分配的页面时,页面缓存中包含许多脏页面,但由于磁盘空间不足而无法写入。在这种情况下,通常所有其他脏页面都会丢失,因为管理员更倾向于在释放一些磁盘空间之前关闭数据库。这会导致严重的损坏。
• 以相对较小的块分配磁盘空间可能导致文件系统级别的显著文件碎片化,并降低大型扫描(例如备份期间)的性能。
使用新的ODS 11.1,Firebird改变了其磁盘空间分配算法,以避免在磁盘空间不足的情况下发生损坏,并给文件系统一个避免碎片化的机会。这些更改如下所述。
a) 每个新分配的页面在返回给引擎之前立即写入磁盘。如果页面无法写入,则分配不会发生,PIP位不会被清除,并会引发相应的IO错误。此错误不会导致损坏,因为我们保证缓存中的所有脏页面都已分配磁盘空间,并且可以安全写入。
与旧行为相比,此更改使每个新分配的页面多了一次写入。因此,在数据库文件增长期间预计会有性能损失。为了减少这种损失,Firebird将新分配页面的写入分组,每次最多128KB,并在PIP头部跟踪“已初始化”页面的数量。
注意:只有当页面首次分配时,新分配的页面才会被写入磁盘两次。也就是说,如果页面被分配、释放并再次分配,则在第二次分配时不会写入两次。
b) 为了避免文件碎片化,Firebird使用了适当的文件系统API,以相对较大的块预分配磁盘空间。目前,此类API仅存在于Windows中,但最近已添加到Linux API中,并可能在将来在诸如ext2等流行的文件系统中实现。因此,此功能目前仅在Firebird的Windows版本中实现,并可能在将来的Linux版本中实现。
为了更好地控制磁盘空间预分配,Firebird.conf中引入了新设置:DatabaseGrowthIncrement。这是预分配块大小的上限(以字节为单位)。默认值为128MB。当引擎需要更多磁盘空间时,它会分配已分配空间的1/16,但不少于128KB,且不超过DatabaseGrowthIncrement值。如果DatabaseGrowthIncrement设置为零,则禁用预分配。数据库影子文件的空间不会被预分配。
此外,如果为数据库设置了“无保留”选项,则预分配也会被禁用。
注意:预分配还可以避免在磁盘空间不足的情况下发生损坏–在这种情况下,数据库很可能有足够的预分配空间来运行,直到管理员释放一些磁盘空间。
作者:Vlad Khorsun,