在SQL Server中,堆表是指没有创建聚集索引的表,其存储空间由PFS,IAM等系统页来跟踪,PFS使用1Byte,表示一个page中空间的使用情况。BTree结构的存储空间是有序的,当向BTree结构中插入新的数据行时,SQL Server按照键值该数据行插入到特定的位置上,以保证BTree结构是有序的;当删除一个Page中的所有数据行之后,SQL Server会将该Empty Page释放,其他对象可以使用该Page。堆表的空间管理,和BTree结构有很大的不同。
1,读取堆表的数据
当需要查询堆表的数据时,SQL Server只能使用表扫描(Table Scan)访问堆表数据。表扫描意味着SQL Server会将访问堆表的所有数据行,以检查该数据行是否满足查询条件。
2,向堆表中插入数据
SQL Server可能将新的数据行插入到表结构的任何page中。在向堆表中插入数据行时,SQL Server扫描PFS系统页,查看堆表的存储空间,只要发现任何一个page有足够的空闲空间能够容纳新的数据行,SQL Server就将数据插入到该位置。如果该Page的存储数据行分布低比较零散,SQL Server会重新组织该数据页上现有数据行的存储,以腾挪出连续的空闲空间,存储新的数据行。
如果已分配的数据页没有足够的空闲空间容纳新的数据行,那么SQL Server会分配新的extent,以存储数据。
3,从堆表中删除数据
当从heap中删除数据行时,SQL Server 2012不会自动组织Page的存储空间,直到插入新的数据行时,SQL Server才会收缩Page中零散的空间,腾挪出连续的空闲空间,以存储新的数据行。如果将Page的所有数据行都删除,SQL Server不会将empty pages的存储空间释放,这部分空间仍然被堆表占用,不能被其他对象使用。
In additin to space on pages, not being reclaimed, empty pages in heaps frequently can not be reclaimed. Even if you delete all the rows from a heap, sql server does not mark the mepty pages as unallocated, so the space is not available for other objects to uses.
在执行删除操作使时,如果使用with(tablock)申请表锁,那么SQL Server在删除数据行时,将释放Empty Page的存储空间。
如果堆表已经存在大量的空闲空间未被释放,有两种方式来解决:
- 执行alter table rebuild命令,重新创建堆表空间,SQL Server将为堆表分配新的存储空间,而释放原有的存储空间,新的存储空间是密集存储的;
- 在堆表上,创建聚集index,将堆表转换为BTree,使用BTree结构管理表空间
4,更新堆表的数据
在更新堆表数据时,如果数据行长度增加,导致数据页不足以容纳更新之后的数据行,那么SQL Server将该数据行移动到其他Data Page上,在原有的Data Page上设置一个Forward Pointer,指向数据行移动之后的物理位置,这样做的好处是,堆表数据的更新不会影响nonclustered index。
如果数据行再次移动,那么Forward Pointer会执行新的物理位置,Forward Pointer会指向数据行的最新的物理位置。如果数据行更新之后,原有的存储空间能够容纳,那么SQL Server移除Forward Pointer,将数据行移动到原有的物理位置上。
Forward Pointer利于堆表数据的更新,但是,不利于堆表数据的查询。SQL Server通过Forward Pointer获取数据行的物理位置,再路由到相应的物理位置去读取数据行的数据,额外增加一次Disk的查询操作。
移除Forward Pointer的操作:
- 收缩数据文件,执行dbcc shrinkfile命令,SQL Server将移除堆表的所有Forward Pointer
- 在堆表上创建clustered index
- 将Forward Pointer指向的数据行删除
- 在执行更新命令时,数据行宽度变窄,SQL Server移除Forward Pointer,将数据行移动到原有的物理位置上
- 重建堆表,执行alter table rebuild命令,重新分配堆表的存储空间
5,查看堆表中Forward Pointer的数量
在SQL Server中,Index ID是0,表示堆表结构;Index ID是1,表示Clustered Index。