从 TFS 迁移到 VSTS - 减少 TPC 数据库大小

Migrating from TFS to VSTS - Reducing TPC Database Size

我们计划很快从本地 TFS 实例迁移到 VSTS。在迁移之前,我们 运行 先决条件验证任务并获得了以下关于 TPC 数据库大小的输出报告:

"数据库当前为 191GB。这超过了使用 DACPAC 导入方法的建议大小 150GB。最大 table 大小目前为 172GB。这超过了建议大小20GB 以使用 DACPAC 导入方法。

验证已完成 'Validate Collection Database Size',结果为警告,消息 当前最大 table 大小为 172GB。这超过了使用 DACPAC 导入方法的建议大小 20GB。"

因此,我们热衷于减小 TPC 数据库的大小,主要有两个考虑因素:

  1. 收缩数据库并从结果输出生成 DACPAC。

  2. 删除以下任何未使用或多余的对象:

    a) 旧工作区
    b) 构建结果
    c) 冗余团队项目
    d) 未使用的文件
    e) 测试期间创建的测试附件 运行s
    f) XAML 构建

因此,希望就这两种方法的优缺点提供一些建议或反馈,并推荐使用哪种方法。

鉴于您需要将最大的 table 减少 150GB,我想知道 DACPAC 是否会成为一个选项。也就是说,清理 TFS 实例始终是个好主意。在您设法剥离足够的数据以实际从收缩中获得任何好处之前,您的第一步不会有太大帮助。

你确定的行动确实会有所帮助,most are already documented here. Queries that can aid in detecting where your space is allocated are also found in this recent support ticket

删除旧作品spaces

删除工作space和搁置集可以大大减少迁移和升级时间。使用 tf 命令行或利用 TFS SideKicks 之类的工具来识别和删除这些。

构建结果

不仅是构建结果,而且经常被忽视的实际构建记录会占用相当大的数据量。使用 tfsbuild destroy (XAML) 永久删除构建记录。过去,我遇到过一些客户,他们的数据库中有 180 万 "hidden" 个构建,删除它们会削减相当多的数据。这些记录为仓库保存。

旧团队项目

当然,销毁旧的团队项目可以回馈大量数据。不需要发送到 Azure 的任何内容都有帮助。你也可以考虑 splitting the collection 并留下旧项目。如果您再次需要该数据,这将使您可以选择分离该集合并将其存储在某个地方。

冗余文件

删除的分支是一种非常常见的隐藏大小猪。在 TFVC 中删除东西时,它们实际上并没有被删除,它们只是被隐藏了。查找已删除的文件,尤其是旧的开发或功能分支可以为您提供大量数据。使用 tf destroy 摆脱它们。

您可能还想查找已签入的 nuget 包文件夹,这些文件夹也可以很快积累很多 space。

测试附件

哦,是的,尤其是当您使用测试附件时,这些附件可能会疯狂增长,具体取决于您的 TFS 版本,要么使用 built-in 测试附件清理功能,要么使用 Test Attachment Cleaner from the TFS power tools.

XAML 构建

构建定义本身不会占用大量数据库 space,但构建结果可能。但这些已在上一节中介绍。

Git 存储库

您的 git 存储库中的数据可能由于强制推送或删除的分支而无法再访问。也有可能 Git 中的某些数据可以更有效地打包。要清理您的存储库,您必须在本地克隆它们,清理它们,从 TFS 中删除远程存储库并将清理后的副本推送到新存储库(您可以使用与旧存储库相同的名称)。这样做会破坏对现有构建定义的引用,您将不得不修复它们。当您使用它时,您还可以 运行 BFG repo Cleaner and convert the repositories to enable Git-LFS support 更优雅地处理存储库中的大型二进制文件。

git clone --mirror <<repo>>
# optionally run BFG repo cleaner at thi s point
git reflog expire --expire=now --all 
git gc --prune=now --aggressive
git repack -adf
# Delete and recreate the remote repository with the same name
git push origin --all
git push origin --tags

工作项(附件)

工作项可以收集大量数据,尤其是当人们开始将大型附件附加到它们时。您可以使用 witadmin destroywi 删除带有过大附件的工作项。要保留工作项但删除其附件,您可以从当前工作项中删除附件,然后克隆它。克隆后,销毁旧的工作项,以便清理附件。

您不再需要的旧工作项目(比如 6 年前的冲刺项目)也可以删除。我的同事 Rene 有一个 nice tool that allows you to bulk-destroy by first creating the appropriate work item query.

确保 运行 清理作业

TFS 通常不会直接从数据库中 p运行e 数据,在许多情况下,它只是将内容标记为已删除以进行最新处理。要强制立即进行清理,运行 项目集合数据库中的以下存储过程:

EXEC prc_CleanupDeletedFileContent 1
# You may have to run the following command multiple times, the last
# parameter is the batch size, if there are more items to prune than the 
# passed in number, you will have to run it multiple times
EXEC prc_DeleteUnusedFiles 1, 0, 100000

其他有用的查询

要确定每个部分中存储了多少数据,there are a few useful queries you can run。实际查询取决于您的 TFS 版本,但由于您正在准备迁移,我怀疑您目前使用的是 TFS 2017 或 2018。

找到最大的 tables:

SELECT TOP 10 o.name, 
SUM(reserved_page_count) * 8.0 / 1024 SizeInMB,
SUM(CASE 
WHEN p.index_id <= 1 THEN p.row_count
ELSE 0
END) Row_Count
FROM sys.dm_db_partition_stats p
JOIN sys.objects o
ON p.object_id = o.object_id
GROUP BY o.name
ORDER BY SUM(reserved_page_count) DESC

找到最大的内容贡献者:

SELECT Owner = 
CASE
WHEN OwnerId = 0 THEN 'Generic' 
WHEN OwnerId = 1 THEN 'VersionControl'
WHEN OwnerId = 2 THEN 'WorkItemTracking'
WHEN OwnerId = 3 THEN 'TeamBuild'
WHEN OwnerId = 4 THEN 'TeamTest'
WHEN OwnerId = 5 THEN 'Servicing'
WHEN OwnerId = 6 THEN 'UnitTest'
WHEN OwnerId = 7 THEN 'WebAccess'
WHEN OwnerId = 8 THEN 'ProcessTemplate'
WHEN OwnerId = 9 THEN 'StrongBox'
WHEN OwnerId = 10 THEN 'FileContainer'
WHEN OwnerId = 11 THEN 'CodeSense'
WHEN OwnerId = 12 THEN 'Profile'
WHEN OwnerId = 13 THEN 'Aad'
WHEN OwnerId = 14 THEN 'Gallery'
WHEN OwnerId = 15 THEN 'BlobStore'
WHEN OwnerId = 255 THEN 'PendingDeletion'
END,
SUM(CompressedLength) / 1024.0 / 1024.0 AS BlobSizeInMB
FROM tbl_FileReference AS r
JOIN tbl_FileMetadata AS m
ON r.ResourceId = m.ResourceId
AND r.PartitionId = m.PartitionId
WHERE r.PartitionId = 1
GROUP BY OwnerId
ORDER BY 2 DESC

如果文件容器是问题所在:

SELECT CASE WHEN Container = 'vstfs:///Buil' THEN 'Build'
WHEN Container = 'vstfs:///Git/' THEN 'Git'
WHEN Container = 'vstfs:///Dist' THEN 'DistributedTask'
ELSE Container 
END AS FileContainerOwner,
SUM(fm.CompressedLength) / 1024.0 / 1024.0 AS TotalSizeInMB
FROM (SELECT DISTINCT LEFT(c.ArtifactUri, 13) AS Container,
fr.ResourceId,
ci.PartitionId
FROM tbl_Container c
INNER JOIN tbl_ContainerItem ci
ON c.ContainerId = ci.ContainerId
AND c.PartitionId = ci.PartitionId
INNER JOIN tbl_FileReference fr
ON ci.fileId = fr.fileId
AND ci.DataspaceId = fr.DataspaceId
AND ci.PartitionId = fr.PartitionId) c
INNER JOIN tbl_FileMetadata fm
ON fm.ResourceId = c.ResourceId
AND fm.PartitionId = c.PartitionId
GROUP BY c.Container
ORDER BY TotalSizeInMB DESC