为什么我得到“数据库'tempdb的日志文件'已满”

时间:2012-08-09 03:06:36

标签: sql sql-server sql-server-2000 sql-execution-plan

让我们有一个付款表,其中35列包含主键(autoinc bigint)和3个非聚集的非唯一indeces(每个列在一个int列上)。

在表的列中,我们有两个日期时间字段:

  1. payment_date datetime NOT NULL

  2. edit_date datetime NULL

  3. 该表约有1 200 000行。 只有~1000行有edit_date column = null。 9000行的edit_date不为null且不等于payment_date 其他人有edit_date = payment_date

    当我们运行以下查询1

    select top 1 *
    from payments
    where edit_date is not null and (payment_date=edit_date or payment_date<>edit_date)
    order by payment_date desc
    

    enter image description here

    服务器需要几秒钟才能完成。但是如果我们运行查询2

    select top 1 *
    from payments
    where edit_date is not null
    order by payment_date desc
    

    enter image description here

    执行结束时数据库'tempdb'的日志文件已满。备份数据库的事务日志以释放一些日志空间。

    如果我们用某些列替换*,请参阅查询3

    select top 1 payment_date
    from payments
    where edit_date is not null
    order by payment_date desc
    

    enter image description here

    它也会在几秒钟内完成。

    魔法在哪里?

    修改 我已经更改了查询1,以便它在与第二个查询完全相同的行数上运行。并且它仍会在一秒钟内返回,而查询2则填充tempdb。

    ANSWER 我按照建议添加索引,为两个日期字段执行此操作 - 一切都按预期开始快速运行。虽然,问题是 - 为什么在这种情况下,sql server在类似的查询上表现不同(查询1与查询2);我想了解服务器优化的逻辑。我同意如果两个查询都使用了类似的tempdb,但他们没有....

    最后,我标记为第一个答案,在那里我看到了问题的必然症状,第一个,以及关于如何避免这种情况的想法(即凹痕)

2 个答案:

答案 0 :(得分:5)

这种情况正在发生,因为执行计划中的某些步骤可能会触发对tempdb的写入,特别是涉及大量数据的某些sortsjoins

由于您正在使用列载荷列对表进行排序,因此SQL决定在没有关联数据的情况下在temp db中单独执行排序会很疯狂。如果它这样做,它将需要在底层表上执行gazzilion低效的书签查找。

遵循以下规则:

  1. 尝试仅选择您需要的数据
  2. 适当地调整tempdb的大小,如果你需要做一个对gazzilion行进行排序的疯狂查询,你最好有一个适当大小的tempdb

答案 1 :(得分:4)

通常,当磁盘空间不足或者为数据库增长设置了不合理的低最大大小时,tempdb会填满。 许多人认为tempdb仅用于#temp表。实际上,您可以轻松填充tempdb而无需创建单个临时表。其他一些可能导致tempdb填满的场景:

  • 任何需要比分配给SQL的内存更多的排序 服务器将被强制在tempdb中完成其工作;
  • 如果排序需要的空间比分配给tempdb的空间多, 将出现上述错误之一;
  • DBCC CheckDB('任何数据库')将在tempdb-on中执行其工作 更大的数据库,这可以消耗相当多的空间;
  • DBCC DBREINDEX或具有'在tempdb中排序'选项的类似DBCC命令 set也可能填满tempdb;
  • 涉及工会的大型结果集,按组/顺序排列,笛卡儿 连接,外连接,游标,临时表,表变量和 散列通常需要tempdb的帮助;
  • 任何未提交但未回滚的交易都可以离开 在tempdb中孤立的对象;
  • 使用带有“创建临时存储”选项的ODBC DSN 程序集可以在那里留下物品 连接。

    使用tempdb         GO

        SELECT name 
            FROM tempdb..sysobjects 
    
        SELECT OBJECT_NAME(id), rowcnt 
            FROM tempdb..sysindexes 
            WHERE OBJECT_NAME(id) LIKE '#%' 
            ORDER BY rowcnt DESC
    

行数越高,值可能表示占用空间的最大临时表。

短期修复

DBCC OPENTRAN -- or DBCC OPENTRAN('tempdb')
DBCC INPUTBUFFER(<number>)
KILL <number>

长期预防

-- SQL Server 7.0, should show 'trunc. log on chkpt.' 
-- or 'recovery=SIMPLE' as part of status column: 

EXEC sp_helpdb 'tempdb' 

-- SQL Server 2000, should yield 'SIMPLE': 

SELECT DATABASEPROPERTYEX('tempdb', 'recovery')
ALTER DATABASE tempdb SET RECOVERY SIMPLE

参考:https://web.archive.org/web/20080509095429/http://sqlserver2000.databases.aspfaq.com:80/why-is-tempdb-full-and-how-can-i-prevent-this-from-happening.html
其他参考文献:http://social.msdn.microsoft.com/Forums/is/transactsql/thread/af493428-2062-4445-88e4-07ac65fedb76