由于文件组“DEFAULT”中的磁盘空间不足,面临无法为数据库“TEMPDB”分配新页面

Posted

技术标签:

【中文标题】由于文件组“DEFAULT”中的磁盘空间不足,面临无法为数据库“TEMPDB”分配新页面【英文标题】:Facing Could not allocate a new page for database 'TEMPDB' because of insufficient disk space in filegroup 'DEFAULT' 【发布时间】:2019-03-13 14:20:22 【问题描述】:

由于文件组“DEFAULT”中的磁盘空间不足,无法为数据库“TEMPDB”分配新页面。通过删除文件组中的对象、向文件组中添加其他文件或为文件组中的现有文件设置自动增长来创建必要的空间。

在执行具有近 2.5 亿条记录的选择语句的存储过程时遇到此错误。

即使我在 tempdb 中有大约 650gb 的空间,我也面临这个错误。想知道我能在这方面做些什么

查询:

SELECT u.Id, u.place, u.name, u.lname, LOWER(ue.email) AS Email, MIN(dp.BirthTime) AS Time
FROM tableA u
    JOIN tableB ue ON ue.id = u.id AND ue.Did = 0
    JOIN tableC dp ON dp.Id = u.Id
        JOIN tableB dpe ON dpe.Id = dp.Id
            LEFT JOIN tableB idpe ON idpe.Email = dpe.Email
                LEFT JOIN tableE idp ON idp.Id = idpe.Id
                    LEFT JOIN tableD pidp ON pidp.Id = idp.Id 
        JOIN tableD cp ON dp.Id = cp.Id
where ISNULL(cp.FName,'') = '' AND ISNULL(cp.LName,'') = '' AND ISNULL(cp.IsActive,0) = 0 AND ISNULL(dp.Isinactive,0) = 0
    AND ISNULL(pidp.FName,'') = '' AND ISNULL(pidp.LName,'') = ''
    AND ISNULL(pidp.Isactive,0) = 0 AND ISNULL(idp.IsInactive,0)
    AND ISNULL(u.EHome, '') != ''
GROUP BY u.Id, u.Isactive, u.name, u.EServer, ue.Email

【问题讨论】:

在此处粘贴您的执行计划。有东西溢出到 tempdb、散列或排序。 这 2.5 亿行有多少空间?这并不是一个很大的查询,但如果这是拉入 varbinary(max) 或其他大行,那么 650gb 很容易被吞噬。 查询有4个join和3个left join,我该怎么办? 请提供查询执行计划:***.com/questions/7359702/…. @Alex 你能告诉我如何为数据库“TEMPDB”分配一个新页面,因为文件组“DEFAULT”中的磁盘空间不足 【参考方案1】:

TempDB 不是问题。

查询有多个带有非 SARGable 表达式的过滤器。必须先处理所有连接,然后才能过滤任何 where 子句数据。如果您的最终结果(过滤和分组)是 2.5 亿行,我预计连接后的结果将达到数十亿。该结果必须在 tempdb 中具体化,然后才能进行过滤和分组。

尝试将此ISNULL(cp.FName,'') = '' 更改为此(cp.FName = '' OR cp.FName IS NULL),以及所有类似的表达式。这至少可以允许在连接之前进行一些过滤。

第二步是删除结果集中没有使用的连接。您只从表 U、UE 和 DP 中返回数据。将其他内部连接移动到存在子句中

EXISTS (SELECT 1 FROM tableB dpe WHERE dpe.Id = dp.Id)

这会将初始加入产品减少为更易于管理的产品。

您的左连接将更难以解决,但如果不是结果集的一部分,也不应该是连接。我阅读了您的查询,说您想过滤缺少数据的结果。例如,只有当 FName 为空字符串、空值或没有匹配记录时,FName 才应返回。另一种说法是您不希望看到 FName、LName、IsActive 填充了非零/非空白值的记录。

AND NOT EXISTS ( SELECT 1 FROM tableD AS pidp where pidp.Id = idp.Id AND fname <> '' AND lname <> '' AND isactive = 0)

如果所有 3 个都已填充,则此语句将排除它们。如果您只想在任一字段有值时排除它们,请将 EXISTS 子句中的 AND 更改为 OR

尝试这些更改,我希望您可以完全避免臃肿的 TempDB。

【讨论】:

非常感谢,它帮助很大,更改后查询需要 15 分钟才能运行。非常感谢您的快速帮助 很高兴它有帮助。如果已解决,您能否选择一个答案以关闭问题?【参考方案2】:

我可以看到查询的一些问题会使其效率非常低:

    LEFT JOIN tableD pidp - 你离开加入这个表,然后在 WHERE 子句中应用过滤条件:

    ISNULL(pidp.FName,'') = ''AND ISNULL(pidp.LName,'') = '' AND ISNULL(pidp.Isactive,0) = 0

ISNULL([col name], '') != '' - 确保永远不会使用列上的任何索引(如果存在)的非常低效的构造。 如果列不可为空,则将过滤条件移至联接并删除 ISNULL

ON pidp.Id = idp.Id AND pidp.FName = ''
    AND pidp.LName = '' AND pidp.Isactive = 0

这也适用于ISNULL(idp.IsInactive,0) 参数。

    GROUP BY 用于修复弱连接/错误查询设计引起的问题的声明 - 这是一个非常糟糕的查询设计的标志,并且很可能是临时表使用过多的唯一原因。

我会按照这些思路重新编写查询(我没有在步骤 1 中包含建议,因为它们取决于您的特定表定义):

SELECT u.Id, u.place, u.name, u.lname, LOWER(ue.email) AS Email, MIN(dp.BirthTime) AS Time
FROM tableA u
    JOIN tableB ue ON ue.id = u.id AND ue.Did = 0
WHERE ISNULL(u.EHome, '') != ''
    AND u.Id IN(
        SELECT dp.id
        FROM tableC dp
            JOIN tableB dpe ON dpe.Id = dp.Id
                LEFT JOIN tableB idpe ON idpe.Email = dpe.Email
                    LEFT JOIN tableE idp ON idp.Id = idpe.Id
                        LEFT JOIN tableD pidp ON pidp.Id = idp.Id 
            JOIN tableD cp ON dp.Id = cp.Id
        where ISNULL(cp.FName,'') = '' AND ISNULL(cp.LName,'') = '' AND ISNULL(cp.IsActive,0) = 0 AND ISNULL(dp.Isinactive,0) = 0
            AND ISNULL(pidp.FName,'') = '' AND ISNULL(pidp.LName,'') = ''
            AND ISNULL(pidp.Isactive,0) = 0 AND ISNULL(idp.IsInactive,0)
         )

【讨论】:

以上是关于由于文件组“DEFAULT”中的磁盘空间不足,面临无法为数据库“TEMPDB”分配新页面的主要内容,如果未能解决你的问题,请参考以下文章

虚拟机里磁盘空间不足怎么弄

可以使用 Java 程序的退出代码来检测磁盘空间不足的异常吗?

由于磁盘空间不足,做软链接转移mongodb数据目录

数据库数据恢复SQL Server数据库所在磁盘分区空间不足报错的数据恢复案例

数据文件resize回收空间

因为 'PRIMARY' 文件组已满。请删除不需要的文件、删除文件组中的对象?