【问题标题】:Long running INSERT query on SQL Server 2005SQL Server 2005 上长时间运行的 INSERT 查询
【发布时间】:2012-02-07 12:25:01
【问题描述】:

我最近一直在寻找论坛和博客,但需要一些帮助来处理长期运行的查询。它是存储过程系统的一部分。此指定语句过去运行大约 5 分钟,但最近已运行长达 72 小时!

设置如下:

具有 28GB 内存的 SQL Server 2005。 SAN 的两个挂载点,共享磁盘由 10 个心轴组成。数据在一个挂载点上,登录在另一个,tempdb 在数据空间上。此服务器上只有一个用户数据库。

这里有两张表,conditcondmodCondit 包含 800K 记录,condmod 最初为空。出于测试目的,我在进程开始之前发出截断 mcmain.condmod。

IF  EXISTS (SELECT * FROM dbo.sysobjects WHERE id = OBJECT_ID(N'[DF__condit__con_notm__000AF8CF]') AND type = 'D')
BEGIN
   ALTER TABLE [mcmain].[condit] DROP CONSTRAINT [DF__condit__con_notm__000AF8CF]
END
GO

/****** Object:  Table [mcmain].[condit]    Script Date: 02/07/2012 11:57:47 ******/
IF  EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[mcmain].[condit]') AND type in (N'U'))
  DROP TABLE [mcmain].[condit]
GO

/****** Object:  Table [mcmain].[condit]    Script Date: 02/07/2012 11:57:49 ******/
IF NOT EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[mcmain].[condit]') AND type in (N'U'))
BEGIN
CREATE TABLE [mcmain].[condit](
    [con_levgln] [char](13) NULL,
    [con_stat] [char](4) NULL,
    [con_dscgrp] [char](35) NULL,
    [con_levart] [char](20) NULL,
    [con_desc] [char](50) NULL,
    [con_disc1] [numeric](5, 0) NULL,
    [con_disc2] [numeric](5, 0) NULL,
    [con_disc3] [numeric](5, 0) NULL,
    [con_ntprce] [numeric](9, 0) NULL,
    [con_dtstrt] [datetime] NULL,
    [con_dtend] [datetime] NULL,
    [con_volc] [char](8) NULL,
    [con_updnmr] [char](20) NULL,
    [con_notmod] [bit] NULL,
    [con_ascver] [char](5) NULL,
    [con_prddat] [datetime] NULL,
    [con_cusgln] [char](13) NULL,
    [con_cusdeb] [char](40) NULL,
    [con_rowid] [int] IDENTITY(1,1) NOT NULL
) ON [PRIMARY]
END
GO

IF NOT EXISTS (SELECT * FROM dbo.sysobjects WHERE id = OBJECT_ID(N'[DF__condit__con_notm__000AF8CF]') AND type = 'D')
BEGIN
  ALTER TABLE [mcmain].[condit] ADD  DEFAULT ((0)) FOR [con_notmod]
END    
GO

IF  EXISTS (SELECT * FROM dbo.sysobjects WHERE id = OBJECT_ID(N'[DF__condmod__com_not__7E22B05D]') AND type = 'D')
BEGIN
  ALTER TABLE [mcmain].[condmod] DROP CONSTRAINT [DF__condmod__com_not__7E22B05D]
END
GO

/****** Object:  Table [mcmain].[condmod]    Script Date: 02/07/2012 11:57:56 ******/
IF  EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[mcmain].[condmod]') AND type in (N'U'))
  DROP TABLE [mcmain].[condmod]
GO

/****** Object:  Table [mcmain].[condmod]    Script Date: 02/07/2012 11:57:58 ******/
IF NOT EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[mcmain].[condmod]') AND type in (N'U'))
BEGIN
CREATE TABLE [mcmain].[condmod](
    [com_levgln] [char](13) NULL,
    [com_stat] [char](4) NULL,
    [com_dscgrp] [char](35) NULL,
    [com_levart] [char](20) NULL,
    [com_desc] [char](50) NULL,
    [com_disc1] [numeric](5, 0) NULL,
    [com_disc2] [numeric](5, 0) NULL,
    [com_disc3] [numeric](5, 0) NULL,
    [com_ntprce] [numeric](9, 0) NULL,
    [com_dtstrt] [datetime] NULL,
    [com_dtend] [datetime] NULL,
    [com_volc] [char](8) NULL,
    [com_updnmr] [char](20) NULL,
    [com_notmod] [bit] NULL,
    [com_ascver] [char](8) NULL,
    [com_prddat] [datetime] NULL,
    [com_cusgln] [char](13) NULL,
    [com_cusdeb] [char](40) NULL,
    [com_rowid] [int] IDENTITY(1,1) NOT NULL
) ON [PRIMARY]
END
GO

IF NOT EXISTS (SELECT * FROM dbo.sysobjects WHERE id = OBJECT_ID(N'[DF__condmod__com_not__7E22B05D]') AND type = 'D')
BEGIN
  ALTER TABLE [mcmain].[condmod] ADD  DEFAULT ((0)) FOR [com_notmod]
END
GO

这是运行很长时间的隔离代码:

DECLARE @TempIdTable TABLE ([com_rowid] Int PRIMARY KEY)

INSERT @TempIdTable([com_rowid])
    SELECT cmd.[com_rowid]
    FROM [mcmain].[condmod] AS cmd
    LEFT OUTER JOIN [mcmain].[condit] AS cdt
      ON con_levgln = com_levgln
     AND IsNull(con_dscgrp,'')  = IsNull(com_dscgrp,'')
     AND IsNull(con_levart,'')  = IsNull(com_levart,'')
     AND IsNull(con_volc,'')    = IsNull(com_volc,'')
     AND IsNull(con_cusgln,'')  = IsNull(com_cusgln,'')
     AND IsNull(con_cusdeb,'')  = IsNull(com_cusdeb,'')
    WHERE con_levgln is NULL

    --select * from @TempIdTable
INSERT INTO mcmain.condit(con_levgln
                ,con_stat
                ,con_dscgrp
                ,con_levart
                ,con_desc
                ,con_disc1
                ,con_disc2
                ,con_disc3
                ,con_ntprce
                ,con_dtstrt
                ,con_dtend
                ,con_volc
                ,con_notmod
                ,con_updnmr
                ,con_ascver
                ,con_cusgln
                ,con_cusdeb)
        SELECT  com_levgln
                ,com_stat
                ,com_dscgrp
                ,com_levart
                ,com_desc
                ,com_disc1
                ,com_disc2
                ,com_disc3
                ,com_ntprce
                ,com_dtstrt
                ,com_dtend
                ,com_volc
                ,com_notmod
                ,com_updnmr
                ,com_ascver
                ,com_cusgln
                ,com_cusdeb
        FROM [mcmain].[condmod] AS cmd
        INNER JOIN @TempIdTable AS tit
          ON tit.com_rowid = cmd.com_rowid

插入@TempIdTable 需要很长时间。我可以做些什么来加快这个过程?

TIA

Cees 无伴奏合唱

附言我在两个表上都有聚集索引,例如:

/****** Object:  Index [condmodTest]    Script Date: 02/07/2012 13:24:34 ******/
IF NOT EXISTS (SELECT * FROM sys.indexes WHERE object_id = OBJECT_ID(N'[mcmain].[condmod]') AND name = N'condmodTest')
CREATE CLUSTERED INDEX [condmodTest] ON [mcmain].[condmod] 
(
    [com_levgln] ASC,
    [com_dscgrp] ASC,
    [com_levart] ASC,
    [com_volc] ASC,
    [com_cusgln] ASC,
    [com_cusdeb] ASC
)WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]
GO

如果我直接选择(刚刚),需要 2 秒。

代码如下:

SELECT  com_levgln
        ,com_stat
        ,com_dscgrp
        ,com_levart
        ,com_desc
        ,com_disc1
        ,com_disc2
        ,com_disc3
        ,com_ntprce
        ,com_dtstrt
        ,com_dtend
        ,com_volc
        ,com_notmod
        ,com_updnmr
        ,com_ascver
        ,com_cusgln
        ,com_cusdeb
FROM mcmain.condmod
LEFT OUTER JOIN mcmain.condit 
    ON  con_levgln  = com_levgln
    AND IsNull(con_dscgrp,'')   = IsNull(com_dscgrp,'')
    AND IsNull(con_levart,'')   = IsNull(com_levart,'')
    AND IsNull(con_volc,'')     = IsNull(com_volc,'')
    AND IsNull(con_cusgln,'')   =   IsNull(com_cusgln,'')
    AND IsNull(con_cusdeb,'')   =   IsNull(com_cusdeb,'')
WHERE   con_levgln is NULL

我刚刚使用以下代码做了一个实际执行计划。花了 3'16'' ???

truncate table mcmain.condit

CREATE TABLE #TempIdTable ([com_rowid]  Int PRIMARY KEY)
--      DECLARE @TempIdTable TABLE
--          ([com_rowid]    Int PRIMARY KEY)

        INSERT #TempIdTable
            ([com_rowid])
        SELECT cmd.[com_rowid]
        FROM [mcmain].[condmod] AS cmd
        LEFT OUTER JOIN [mcmain].[condit] AS cdt
          ON con_levgln = com_levgln
         AND IsNull(con_dscgrp,'')  = IsNull(com_dscgrp,'')
         AND IsNull(con_levart,'')  = IsNull(com_levart,'')
         AND IsNull(con_volc,'')    = IsNull(com_volc,'')
         AND IsNull(con_cusgln,'')  = IsNull(com_cusgln,'')
         AND IsNull(con_cusdeb,'')  = IsNull(com_cusdeb,'')
        WHERE con_levgln is NULL
        -- AND      com_updnmr = @plannummer

            INSERT INTO mcmain.condit
                    (con_levgln
                    ,con_stat
                    ,con_dscgrp
                    ,con_levart
                    ,con_desc
                    ,con_disc1
                    ,con_disc2
                    ,con_disc3
                    ,con_ntprce
                    ,con_dtstrt
                    ,con_dtend
                    ,con_volc
                    ,con_notmod
                    ,con_updnmr
                    ,con_ascver
                    ,con_cusgln
                    ,con_cusdeb)
            SELECT  com_levgln
                    ,com_stat
                    ,com_dscgrp
                    ,com_levart
                    ,com_desc
                    ,com_disc1
                    ,com_disc2
                    ,com_disc3
                    ,com_ntprce
                    ,com_dtstrt
                    ,com_dtend
                    ,com_volc
                    ,com_notmod
                    ,com_updnmr
                    ,com_ascver
                    ,com_cusgln
                    ,com_cusdeb
            FROM [mcmain].[condmod] AS cmd
            INNER JOIN #TempIdTable AS tit
              ON tit.com_rowid = cmd.com_rowid

相同的语句,但使用表变量需要 1'39''

当我运行 sp 时,同样的语句需要几个小时。还是不明白。

【问题讨论】:

  • 顺便说一下,我在两个表上都有聚集索引:
  • SELECT 自身需要多长时间?
  • 如果您设置 SET ANSI_NULLS OFF 并删除 isnull 会更快吗?
  • Martin,如果我使用左外连接直接选择,则需要两秒钟 :(
  • @ceescap - 如果您单独查看SELECT 的执行计划,它是否有并行计划?

标签: sql-server-2005 tsql


【解决方案1】:

如果SELECT 本身需要很长时间

您可以考虑使用NOT EXISTS 而不是OUTER JOIN ... NULL,因为这通常更有效。

我也会摆脱不可分割的ISNULL 比较。

SELECT cmd.[com_rowid] 
FROM   [mcmain].[condmod] AS cmd 
WHERE  NOT EXISTS (SELECT * 
                   FROM   [mcmain].[condit] AS cdt 
                   WHERE  con_levgln = com_levgln 
                          AND EXISTS (SELECT con_dscgrp, 
                                             con_levart, 
                                             con_volc, 
                                             con_cusgln, 
                                             con_cusdeb 
                                      INTERSECT 
                                      SELECT com_dscgrp, 
                                             com_levart, 
                                             com_volc, 
                                             com_cusgln, 
                                             com_cusdeb)) 

如果SELECT 自己运行得很快,但在插入表变量时却没有,那么请检查在快速情况下您是否获得并行计划。

插入到表变量的查询不会被并行化,所以如果这是您可以考虑更改为 #temp 表的问题。

如果这些建议都没有帮助,那么我建议您在此过程运行时开始监控等待类型。见论文"SQL Server 2005 Waits and Queues"

【讨论】:

  • @ceescap - 您运行插入表变量的相同代码需要 7 秒?
  • 您如何确定该特定陈述是问题之一? INSERT INTO mcmain.condit 也可能受益于使用#TEMP 表而不是@table_var,因为它可以使用统计信息。由于单行假设,连接到表变量的查询的执行计划有时会非常糟糕。
  • Martin,我在我的代码中穿插了写到 translog 表的语句,包括日期和时间。这样我就知道哪个语句在什么时候开始以及从 sp 中运行需要多长时间。
  • 测试表明@Temp 比#temp 表快。
  • @ceescap - 您需要确定它之所以慢是因为您的计划不好,还是因为它被阻止等待某事。
【解决方案2】:

注意:@tables 的使用使用 tempdb。

您应该尝试增加临时 db 文件的数量...您可以从等于您拥有的 proc 数量的 tempdb 文件的数量开始...但作为一个很好的起点,只需跳到 10...您可能遇到的问题是对 tempdb 文件的争用。您应该在 tempdb 中查找页面锁定。

这篇文章讨论了我上面写的概括是一个误解(但不一定是错误的),但这样做很好地解释了这个问题。它还为您指出了其他文章,这些文章提供了有关如何查找页面锁定的信息。

A SQL Server DBA myth a day: (12/30) tempdb should always have one data file per processor core

【讨论】:

  • 这仅适用于 OP 在 tempdb 中创建大量不同对象的情况。在没有遇到分配争用的系统中,这样做弊大于利。
  • 每个处理器核心都有一个 tempdb 文件,即 16 个 tempdb 文件。
  • @MartinSmith 我认为您假设这是当时数据库中唯一发生的事情。其他人可能同时使用表变量。不管怎样,理论都无所谓。看看经验证据。 ceescap 你应该看看你是否有文件争用。如果你不这样做,那么这不是问题。如果你这样做......那么你就会知道要专注于提高性能。修复文件争用或停止使用表变量来避免问题。 -GL
  • 我没有假设任何事情。我的评论没有说这个不能适用。不过同意经验证据。确定是计划问题还是等待问题(如果是,等待什么)是前进的方向。
  • 出于好奇,请解释一下可能造成的危害?
猜你喜欢
  • 1970-01-01
  • 2014-01-23
  • 1970-01-01
  • 1970-01-01
  • 2014-03-09
  • 2023-03-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多