与简单地重建每个索引相比,ALTER INDEX ALL REBUILD是否使用具有简单恢复模型的事务日志空间更多?


18

由于事务日志空间不足,SQL Server 2012上的“ ALTER INDEX ALL REBUILD”操作失败。索引从未进行过重组或重建,因此几乎所有索引上的碎片都超过80%。

数据库使用简单的恢复模型。我假设在命令的“ ALL”形式执行的每个索引操作之后,在下一次重建索引之前将刷新事务日志数据。这是它的实际工作方式,还是索引重建记录为好像是单个事务的一部分?

换句话说,是否可以通过编写脚本来分别执行每个重建来减少事务日志的增长?还有其他要考虑的因素吗?


2
除非有明确的数据,否则我假设数据库引擎会将特定的SQL命令视为单个原子事务。在这种情况下,您可以相当轻松地检验理论。选择最大的索引,然后尝试重新构建它。如果成功,则可以合理地假设日志正在累积来自多个重建的信息。如果失败,那么您需要为日志添加空间(因为两种方式都存在问题),或者您需要尝试重新组织该索引,而不是重新构建(如果您不能为t-日志)。
RDFozz

是的,当我完成此输入(橡皮鸭效果)时,这种想法就发生了。我不想在这种环境下进行实验,因此我可能最终都会以任何一种方式在日志中添加空间。
Google失败

Answers:


16

我假设在命令的“ ALL”形式执行的每个索引操作之后,在下一次重建索引之前将刷新事务日志数据。这是它的实际工作方式,还是索引重建记录为好像是单个事务的一部分?

1)日志刷新:SIMPLE恢复模型不会在每次事务处理后而是在检查点清除日志。(更多信息的链接

2a)全部重建:是的,全部重建作为单个事务工作。索引重建在其中具有自己的事务,但是整个操作要到最后才完全提交。因此,可以的,您可以通过重建单个索引(并可能发出CHECKPOINT命令)来限制日志文件的增长。

2b)证明!在这里,有一个演示脚本。(内置于2016 dev)首先,建立一个带有表和索引的测试数据库:

USE master
GO

CREATE DATABASE Test_RebuildLog
GO

ALTER DATABASE Test_RebuildLog
SET RECOVERY SIMPLE
GO

USE Test_RebuildLog
GO

CREATE TABLE IndexTest
(ID int identity(1,1),
a char(1),
b char(1))

CREATE CLUSTERED INDEX CIX_IndexTest_ID ON IndexTest(ID)
CREATE INDEX IX_IndexTest_a ON IndexTest(a)
CREATE INDEX IX_IndexTest_b ON IndexTest(b)

INSERT IndexTest
(a,b)
VALUES ('a','b'),('z','y'),('s','r')

现在,您可以比较REBUILD ALL和单独重建之间的日志活动

CHECKPOINT
GO
ALTER INDEX ALL ON IndexTest REBUILD

SELECT *
FROM sys.fn_dblog(NULL,NULL)
WHERE Operation = 'LOP_COMMIT_XACT'
OR Operation = 'LOP_BEGIN_XACT'
GO

CHECKPOINT
GO
ALTER INDEX CIX_IndexTest_ID ON IndexTest REBUILD
ALTER INDEX IX_IndexTest_a ON IndexTest REBUILD
ALTER INDEX IX_IndexTest_b ON IndexTest REBUILD

SELECT *
FROM sys.fn_dblog(NULL,NULL)
WHERE Operation = 'LOP_COMMIT_XACT'
OR Operation = 'LOP_BEGIN_XACT'
GO

请注意,直到重新构建全部结束后,才提交第一个未完成的事务(对我来说,事务ID为0000:000002fa),但是对于逐索引重建,它们是连续提交的。


哇,谢谢您的详细答复!可以这么说,这是了解幕后情况的好方法。
Google失败

很好解释。
Ramakant Dadhichi

4

就目前而言,这是单笔交易。


6
欢迎使用DBA.SE!通常,最佳答案不是简单的断言,而是文档或文章中的信息或(通常甚至更好的)个人经验证明的最佳答案。您可以扩展您的答案以提供这种支持吗?
RDFozz

2
@RDFozz Fair评论,但是您看过Pedro的个人资料吗?访问源代码可能比个人经验或文档更具权威性。:-)
亚伦·贝特朗

3
@AaronBertrand-我承认,我没有。我当然认为,成为SQL Server团队的一员确实可以胜任。尽管如此,还是值得在答案中引用它。+1,无论如何。
RDFozz

3

对于离线重建而言,这个问题并不重要。当然是单笔交易。想象一下,如果该操作将每个索引分成自己的事务,将会造成严重破坏,因为在提交时必须释放锁,然后重新获取它们,这将导致该锁。释放关键表SCH-M锁后,可能会删除索引并创建新索引,该语句将如何处理此类情况?更不用说该可能会被删除,甚至在两个事务之间重新创建!包括删除表并使用相同的对象ID创建另一个表的情况(是的,它可能发生)...

如果您扩大问题以说如果索引重建是在线重建会怎样呢?它是单笔交易还是很多笔交易?答案很复杂,因为实际上涉及多个内部事务。但是,关键是存在一个跨整个操作的整体弓形事务(ALTER语句),并且这会将日志固定在适当的位置(无法截断),因此需要相应地计划操作以允许约1.6倍的数据FULL恢复模式的数据大小,或BULK_LOGGED / SIMPLE模式的数据大小的0.2倍。有关更多详细信息,请参见链接的纸张。

您可能会争辩说,为什么脱机构建不使用与在线模式相同的内部事务,并拆分操作?我提到的有关在单个索引操作之间更改/删除表的问题(即表“模式稳定性”)仍将需要有一个包含事务的事务,该事务在整个语句期间将SCH-S保留在表上。由于此事务在恢复期间还必须保留SCH-S,因此必须对其进行记录,因此将存在BEGIN XACT日志记录,该记录将固定日志并在语句的整个持续时间内防止被截断。我知道此特定问题已在SQL 2016-2017时限内解决(由于SQL Azure日志大小问题),但我不确定取得了什么进展。看起来现在正在预览中:可恢复的联机索引重建在SQL Server 2017 CTP 2.0的公共预览中


0

是的,我在很大的桌子上遇到了同样的问题。每当我发出ALTER INDEX ALL时,事务日志都会增长很多,但是如果单独发出ALTER INDEX,则日志空间的使用会较小。


0

Remus先前的回答是:在完全恢复模式下,联机索引需要索引大小的1.6倍是不正确的。在FULL下在线重建索引所需的事务日志空间所占的比例可能更高,而且我们观察到索引的大小是原来的许多倍,尤其是当重建的索引由于未压缩事务日志而被压缩时。仅此一项就应明确表明,在FULL下进行联机重建期间的事务记录至少可以是索引大小的几倍。加上tlog记录开销,Microsoft并未完全记录下来,但通常估计为每行60字节,并且在完全恢复下的联机索引重建期间,按比例记录的日志大小可能是所重建索引大小的许多倍。索引已压缩


-1

Rdfozz是正确的,这是决定是否可以基于当前存储来重建最大索引的最佳方法。只要dm_exec_requests在操作发生时运行(或SQL事件探查器),即可查看是否正在重建所有索引。我还将考虑将恢复模型更改为批量记录。这就是我的工作,并且在窗口期间仍然有事务日志备份。参见下面的文章 https://technet.microsoft.com/zh-cn/library/ms191484(v=sql.105).aspx


2
注意,OP声明其数据库已在使用SIMPLE恢复模型;这只会使事务在日志中保留足够长的时间,以便完成事务。如果他们切换到批量日志记录,将没有任何改善。
RDFozz

你说得很对 我很抱歉。
ADTJOB'7
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.