如何在 MS SQL 服务器数据库中快进@@DBTS?

How to fast-forward @@DBTS in a MS SQL Server database?

在 SQL 服务器中,是否有合法或骇人听闻的方法来覆盖或“快进” @@DBTS 的值? 我能够做到这一点的唯一方法是对循环中具有 timestamp / rowversion 列的 table 进行一些虚拟更新,其中一个循环步骤如下:

SELECT @@DBTS -- original value

-- no-op update that increments @@DBTS nevertheless
UPDATE Table_with_a_rowversion_column SET any_other_column = any_other_column

SELECT @@DBTS -- value has incremented by a number of rows in Table_with_a_rowversion_column

我不喜欢这个,因为它很慢并且占用资源。我想将@@DBTS 递增数十亿,并且速度很快——高达 16^9。我的循环脚本在 30 分钟内递增的次数少于 16^8,因此至少需要 8 小时。

背景/为什么有人想这样做?

我正在将数据从数据库 1 迁移到数据库 2。 它们有不同的模式,但 1 和 2 都有一个“状态”table(大约一百万行)和一个“state_history”table(10 的百万行)。

state table 有一个 rowversion 列。 在 state table 上触发将更新前的数据插入 state_history table(经过一些过滤)- 包括捕获的 rowversion 值(另存为 binary(8)

按 rowversion/binary(8) 列排序的 statestate_history 的联合是按实际发生的顺序对最新和历史值进行排序的唯一可靠方法(捕获的系统时钟并不总是可靠的)。

我想在目标数据库 2 中保持相同的单调递增 rowversion 不变量,系统的很少部分依赖于这个不变量。 我确实可以控制历史行版本,因为它是 binary(8),但它们的范围从 16^5 到 16^9 不等,即没有大的基值可以减去,所以我必须保持原样。

但我无法控制主要目标 state table - SQL 根据目标数据库的 @@DBTS 插入小的 rowversion 值。我希望我可以简单地告诉它把它覆盖到 16^9 然后 运行 我的迁移脚本。

sort-of 轻松获胜。 @@DBTS 将为 INSERT 进行更新,并且它不是真正的 table 所独有的。下面的代码生成一个虚拟 table,然后在 3 分钟内在我的笔记本电脑上插入(二进制)十亿行。要将值更改为 16^9,将需要 Truncate/Insert 部分的 64 次迭代,而 64*3.2“仅”204.8 分钟(3.413)小时,完成后,您的日志文件仍然很小并且您的 PRIMARY 文件组根本不会增长。

请注意,当您 运行 执行此操作时,您需要处于 BULK_LOGGED 或 SIMPLE 恢复模式,否则它会严重破坏您的 T-Log 文件。生成的 HEAP 大小为 18.75GB,但它仅使用 T-LOG.

的 450MB(不是印刷错误...兆字节)

详情见评论。阅读评论!它们很重要!

--===== IMPORTANT! CHANGE ALL OCCURANCES OF THE WORD "SCRATCH" TO THE NAME OF THE DATABASE YOU WANT TO DO THIS IN!!!
     -- ALTHOUGH I"VE NOT TESTED FOR IT, I'M PRETTY SURE IT'S GOING TO KNOCK A FAIR BIT OUT OF BUFFER CACHE, NO MATTER WHAT!
     -- READ AND UNDERSTAND ALL THE COMMENTS IN THE CODE BELOW OR DON'T USE THIS CODE!
     -- TEST IT ON A DEV BOX FIRST!!! 

--===== Create a temporary filegroup and presized file so we don't have to wait for growth.
     -- Yes... the table (HEAP) will reach a size of 18.75GB (aka 18750MB)
    USE Scratch;
  ALTER DATABASE Scratch ADD FILEGROUP Dummy;
  ALTER DATABASE Scratch 
    ADD FILE (NAME     = N'Dummy'
             ,FILENAME = N'E:\SQL Server\SQLData\Dummy.ndf'
             ,SIZE=18750, MAXSIZE = 20000MB, FILEGROWTH=50MB)
     TO FILEGROUP Dummy
;
GO
--===== This creates a table with the minimum row width we can get away with.
 CREATE TABLE dbo.Dummy (N TINYINT, ts TIMESTAMP) ON Dummy; --It's a HEAP, actually
GO
--===== Do a (binary) BILLION inserts into this working table to increase @@DBTS by 1 BILLION (1024^3) for the database.
     -- You could put this in a loop.  The file will not grow and we'll do minimal logging to protect the logfile.
     -- TO GET THE MINIMAL LOGGING, THE DATABASE MUST BE IN THE BULK LOGGED OR SIMPLE RECOVERY MODEL.
     -- OTHERWISE, THIS WILL EXPLODE YOUR LOG FILE.
     -- On my laptop, this section takes 3.2 minutes to run and only uses 450MB thanks to minimal logging.
     -- And, no, that's not a misprint... ONLY 450 Megabytes of log file useage 
        TRUNCATE TABLE dbo.Dummy;  --Only Page De-Allocations are logged and required to continue minimal logging.
        CHECKPOINT; --Forces writes to T-Log file in the BULK LOGGED and to clear in the SIMPLE Recovery Models.
        CHECKPOINT; --Sometimes, one CHECKPOINT isn't fully effective.
;
 SELECT @@DBTS --Just checking the "Before" value.
;
   WITH
  E1(N) AS (SELECT 1 FROM (VALUES (1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1))E0(N)) --Up to 16 rows
            INSERT INTO dbo.Dummy WITH (TABLOCK) --Required to get minimal logging in BULK LOGGED and SIMPLE Recovery Models
                   (N)
            SELECT TOP 1073741824 CONVERT(TINYINT,1) --Binary BILLION (1024^3)
              FROM E1 a,E1 b,E1 c,E1 d,E1 e,E1 f,E1 g,E1 h --Up to 16^8 or 4,294,967,296 rows
            OPTION (RECOMPILE,MAXDOP 1) --Helps minimal logging and prevents parallelism for a substantial speed increase.
;
 SELECT @@DBTS
;
GO
--===== This is the cleanup of the file and filegroup, which is MUCH faster than a SHRINK.
   DROP TABLE dbo.Dummy;
  ALTER DATABASE Scratch REMOVE FILE      Dummy;
  ALTER DATABASE Scratch REMOVE FILEGROUP Dummy;
        CHECKPOINT; --Forces writes to T-Log file in the BULK LOGGED and to clear in the SIMPLE Recovery Models.
        CHECKPOINT; --Sometimes, one CHECKPOINT isn't fully effective.
GO
 SELECT @@DBTS --Verify that the effort wasn't wasted.
;

这是我使用的Scratch数据库的图片。在我最初的测试中,两个文件的大小从 50MB 开始,增长了 50MB,以查看最终大小。您可以看到 PRIMARY 文件组没有增长多少(它只有 4MB,从 1MB 开始)。我们所做的一切都转到了 DUMMY 文件组,我们将在完成后删除它,这样我们就不必收缩我们的 PRIMARY 或担心我们刚刚使用的额外 18.75GB。

在我们运行上面最后一段代码删除DUMMYtable,删除DUMMY文件,删除DUMMY文件组后,数据文件总大小恢复正常,没有不得不收缩。

而且,如果日志文件中只有 450MB 可用,它根本就不会增长。