对于行存储表的示例...
CREATE TABLE T(Id INT PRIMARY KEY, C1 INT NULL, C2 INT NULL);
有多种不同的方法可以从 SQL Server 中的元数据中检索表行数 - 例如以下方法
SELECT SUM(rows)
FROM sys.partitions
WHERE object_id = object_id('dbo.T') AND index_id <= 1;
SELECT SUM(row_count)
FROM sys.dm_db_partition_stats
WHERE object_id = object_id('dbo.T') AND index_id <= 1;
SELECT SUM(rows)
FROM sys.sysindexes
WHERE id = object_id('dbo.T') AND indid <= 1;
SELECT OBJECTPROPERTYEX(object_id('dbo.T'), 'Cardinality')
执行计划显然显示了正在使用的各种不同对象 - 例如下面的。
- sysrowsets OUTER APPLY OpenRowset(TABLE ALUCOUNT
- sysidxstats 交叉应用 OpenRowSet(表分区计数
- sysidxstats i 交叉应用 OpenRowSet(TABLE INDEXPROP
这是怎么回事?SQL Server 真的在多个地方维护这些元数据吗?如果是这样,哪种方法最可靠?
为了研究这个问题,我创建了一个如下所示的新数据库
这创建了一个包含单个数据文件的数据库,我使用进程监视器来监视该数据库上的 WriteFile 操作。然后运行
ProcMon 显示以下页面已写入磁盘
计算偏移量来查看它们所属的对象
已返回
sysallocunits
包含与页数相关的列,但没有与行数相关的列,sysrscols
包含用于基于基数的执行计划重新编译的每列修改计数,但同样没有行数列。这些系统基表中唯一包含行数列rcrows
的是sysrowsets
。因此我得出结论,所有这些方法最终都是从同一个地方获取值,因为它只持久地存储在一个地方。
我还运行了下面的
第一份手册之后的交易日志的相关部分
CHECKPOINT
如下各种
LOP_COUNT_DELTA
条目展示了一些有趣的方面LOP_COMMIT_XACT
用户交易之后(即不属于这些交易的一部分)LOP_COUNT_DELTA
条目设置Row count: 104
)因此,如果 sysrowsets 中的行数仅定期更新,这是否意味着返回的元数据值不正确?
问题中给出的所有方法在写入之前仍然返回了正确的行数。我推测计数是根据各个内部DMF( / / )
sysrowsets
使用的内存结构更新的。ALUCOUNT
PARTITIONCOUNTS
INDEXPROP
我甚至在将更改
sysrowsets
写入磁盘之前终止了 SQL Server 进程,并且它们在服务重启后仍然返回正确的值,因此我认为非检查点增量是作为数据库恢复的一部分重新计算的。记录显示行数可能会出错并需要纠正,但我不确定是什么情况会导致这种情况。
问题中列出的所有方法最终都通过调用来检索行数
sqlmin!GrabRowsetCounts
:sys.partitions
sys.dm_db_partition_stats
sys.sysindexes
OBJECTPROPERTYEX
正如您所注意到的,信息仅保存在一个内部表中。
按照惯例,每个 HoBt 的行集计数都会在内存中维护。对于逐行操作,这实际上意味着每次修改行时都会增加或减少计数器,例如通过调用
sqlmin!BaseSharedHoBt::DeltaRowCount
。LOP_COUNT_DELTA
净变化的日志记录通过以下方式在检查点写入sqlmin!FlushHoBtsDeltaCounts
:请注意,许多方法名称都表明它们是非事务性的。