行版本控制为每行维护 14 个字节的内部版本控制信息。但这真的是每行的成本还是也适用于表中每个索引的成本?
似乎 14 个字节也必须添加到所有索引记录中,以便仅索引扫描(和其他仅索引访问)可以查看版本信息并感知数据的时间点快照。
不过,我在网上能找到的所有信息都只谈到每行 14 字节的开销。
似乎也必须将 14 个字节添加到所有索引记录中
为什么要把它添加到所有索引记录中?你的意思是所有索引,还是所有索引的所有行?
只有受影响索引的受影响行才会获得 14 字节标记。
这里有3个例子。
假设我们有 100 页的聚簇表(有id int PK
)和 1 个非聚簇索引(在另一列上Col1
),并且我们更改id
了聚簇索引的10 个键 ( )。
聚集表的所有 10 行都将获得行版本标记(10 行,而不是 100 行),非聚集索引的 10 个受影响的行将具有此标记,因为每个非聚集索引至少在叶级别包含聚集索引键,因此 10 行非聚集索引也有它们的版本。
您可以通过更改 db 中具有非聚簇索引的聚簇表中的 10 行RCSI
并通过检查来证明这一点sys.dm_tran_version_store
:将有 20 行,您将能够通过它们的大小区分聚簇索引行和非聚簇索引行。
………………………………………………………………………………………………………………………………………………………… .
现在假设我们在同一列上有 100 行和非聚集索引的同一个Col1
表。现在我们做一个update
的id
这列堆。
如果您现在检查,sys.dm_tran_version_store
您只会看到 10 行,它们是堆的 10 行,根本没有索引行。这是因为Col1
当我们更新 10 时,我们的索引没有改变id
。id
在这种情况下,我们对在叶级别将 RID 作为指向堆的反向指针的非聚集索引没有影响。
当然,如果我们的非聚集索引有id
作为键字段或包含字段,update
ofid
也会影响这个索引,在这种情况下,我们又找到了 20 行row version store
。
………………………………………………………………………………………………………………………………………………………… .
示例 N3:具有 Col1、Col2、Col3 结构的表。索引:ix_1 (Col1)、ix_3(Col3)。
我们更新没有出现在任何索引中的 Col2。只有表中受影响的行才会获得行版本标签,ix_1 和 ix_3 根本不会受到影响。
正如 Solomon Rutzky 在他的评论中指出的那样,这些版本标签将出现在索引中,直到INDEX REBUILD
.
以下是本书的引用:Dmitri Korotkevitch 的 Pro SQL Server Internals
乐观的隔离级别会导致索引碎片化。当一行被修改时,由于版本控制标记指针,SQL Server 将行大小增加 14 个字节。如果一个页面被紧密打包并且该行的新版本不适合该页面,则会导致页面拆分和进一步的碎片化。这与我们在第 6 章“索引碎片”中讨论的插入/更新模式非常相似。即使从版本存储中删除记录,这 14 个字节仍将保留在行中,直到重建索引。
如果使用乐观隔离级别,建议您使用小于 100 的 FILLFACTOR 在页面上保留一些空间。它减少了由于版本存储指针导致行大小增加而导致的页面拆分。