在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。
forwarded_record_count:Number of records in a heap that have forward pointers to another data location. (This state occurs during an update, when there is not enough room to store the new row in the original location.)
参考文档: