我们在 Azure SQL 数据库中有一个表,该表曾经有一个存储 pdf 文件的 nvarchar(max) 列。(外部开发人员的奇迹。)表增长到 156 GB。它有 476000 行。更改逻辑后,我们不再需要 pdf 列。删除其中数据的唯一合理方法是删除该列并重新创建该列(以防某些奇怪的进程仍在引用它)。
但是,表大小仍报告为 156 GB。我刚刚创建的备份表 (SELECT INTO) 是 128 MB,因此这似乎是数据的实际大小。
我让索引重建(在线)在聚集 PK 索引上运行过夜。它在 8 到 12 小时之间的某个时间因 TCP 错误而失败。该索引仍存在 95% 的碎片,报告的大小仍为 156 GB。
有没有解释为什么这么慢?有没有更好的办法?生产数据库、表由网站使用,必须可访问,因此不能离线执行,除非需要不到 10 分钟 - 没有人可以保证。
我可以在备份表上建立所有索引,删除原始表并重命名备份吗?这听起来很冒险(丢失在错误时间创建的记录的风险很小)。
我正在努力让 Azure 意识到它不再被使用。已分配,我对此表示同意。用过,没那么多:
有问题的表:
同样,问题不是保留空间,而是已使用空间。
是的,这是预期的。
为什么?
SQL Server 中空间分配的工作方式是,随着数据的增长,空间会从磁盘分配到 SQL Server 实例中该数据库的内部文件,块的大小是在数据库属性中定义的,在文件 -> 自动增长下设置:
这会主动将数据保留在磁盘之外,并确保将来向该数据库添加更多数据时可用。文件增长操作是相当繁重的操作,这就是为什么数据库文件不以与数据本身增长相同的速率和数量增长的原因。
文件缩小也是如此。这是一项昂贵的手术。默认情况下,数据库设计为在删除数据时不会自动收缩。假设已经从磁盘占用的空间最终将被重新使用。因此,当数据被删除时,它不会将其释放回磁盘,而是在内部进行标记,以便将来的数据覆盖它时重新使用。这听起来可能很浪费,但磁盘很便宜,而性能却不便宜,这是通过在每次数据更改时不不断增长和缩小而隐含获得的。
一个有用的工具是系统存储过程
sp_spaceused
。如果您在特定数据库的上下文中运行它,不带任何参数,它会告诉您该数据库的总大小 -database_size
在第一个结果集中,以及该磁盘空间当前在数据库文件中的分布情况 -reserved
vsdata
vsindex_size
in第二个结果集。以前,列中可能有大约 156 GB 的空间,data
现在将显示该空间reserved
,这意味着它已准备好被未来的数据增长所消耗。现在来解决您的问题
SQL Server 有一个收缩命令(也可以通过 SSMS GUI 执行),它将把数据库文件中的保留空间释放回磁盘(尽可能多地释放,其中有一些if、ands 或 buts)。通常建议不要这样做,因为收缩是对磁盘的繁重操作,并且可能会影响并发数据库性能,直到完成为止,并且因为一旦将更多数据添加到数据库,释放的空间将不得不再次经历繁重的增长操作。但在像您的情况这样的特殊的一次性大数据更改中,如果您确实需要的话,这样做可能是明智的。
边注
索引重建通常是一种浪费和浪费的操作 - 导致文件增长,这基本上会抵消因收缩而释放的一些空间。这是一个恶性循环。如果您这样做是为了提高性能,那么您实际上不太可能从重建本身中获得任何好处(索引碎片在现代硬件上确实不再重要)。重建会触发后续的操作,这些操作可能会提高您的性能,例如清除计划缓存和更新统计信息。但是您可以单独、更精细、更高效地运行这些操作,而无需进行浪费的索引重建。