批量数据加载和事务日志

Ray*_*ond 7 sql-server optimization bulk sql-server-2008-r2 import

我目前正在开发一个项目,该项目从平面文件 (csv) 中批量导入数据,大约 18 个不同的文件通过一些存储过程链接到特定的表。

我按照数据加载性能指南中的建议进行了操作。

数据库处于BulkLogged恢复模式以最小化日志记录,当在包含 600000 行的文件上执行以下存储过程时,我收到错误

消息 9002,级别 17,状态 4,过程 SP_Import__DeclarationClearanceHistory_FromCSV,第 34 行
数据库的事务日志已满。要找出无法重用日志中的空间的原因,请参阅 sys.databases 中的 log_reuse_wait_desc 列

(出于测试目的,我在开始导入之前进行了完整备份)。

看着log_reuse_wait_desc我看到以下内容:

log_reuse_wait_desc 检查点。所有其他导入成功导入。

欢迎任何解决此问题的意见。

PROCEDURE [dbo].[SP_Import_DeclarationClearanceHistory_FromCSV]
    @FilePath [nvarchar](1000)
AS
BEGIN
    -- Creating a Temproary Table for importing the data from csv file.
    DBCC TRACEON(610)

    CREATE TABLE #DeclarationClearanceHistory
    (
          [ItemID] [int] IDENTITY(1, 1) NOT NULL ,
          [CMSDeclarationID] [bigint] NOT NULL ,
          [StatusCode] [nvarchar](10) NOT NULL ,
          [SubStatus] [nvarchar](10) NULL ,
          [DepartmentCode] [nvarchar](10) NULL ,
          [StartDate] [datetime] NULL ,
          [EndDate] [datetime] NULL ,
          PRIMARY KEY CLUSTERED ( [ItemID] ASC )
            WITH ( PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF,
                   IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON,
                   ALLOW_PAGE_LOCKS = ON ) ON [PRIMARY]
        )
    ON  [PRIMARY]

    -- Inserting all the from csv to temproary table using BULK INSERT
    EXEC ('BULK INSERT #DeclarationClearanceHistory
    FROM ''' + @FilePath + '''
    WITH ( FIELDTERMINATOR = ''<,>'', ROWTERMINATOR =''\n'', FIRSTROW = 2, KEEPIDENTITY, CODEPAGE = ''ACP'', ORDER = ''ITEMID ASC'' );') ;

    -- By using MERGE statement, inserting the record if not present and updating if exist.
    MERGE dbo.DeclarationClearanceHistory AS TargetTable                            -- Inserting or Updating the table.
        USING #DeclarationClearanceHistory AS SourceTable                           -- Records from the temproary table (records from csv file).
        ON ( TargetTable.ItemID = SourceTable.ItemID )      -- Defining condition to decide which records are alredy present
        WHEN NOT MATCHED BY TARGET 
            THEN INSERT (
                          ItemID ,
                          CMSDeclarationID ,
                          StatusCode ,
                          SubStatus ,
                          DepartmentCode ,
                          StartDate ,
                          EndDate
                        )
               VALUES   ( SourceTable.ItemID ,
                          SourceTable.CMSDeclarationID ,
                          SourceTable.StatusCode ,
                          SourceTable.SubStatus ,
                          SourceTable.DepartmentCode ,
                          SourceTable.StartDate ,
                          SourceTable.EndDate
                        )
        WHEN MATCHED -- If  matched then UPDATE
            THEN UPDATE
               SET      TargetTable.ItemID = SourceTable.ItemID ,
                        TargetTable.CMSDeclarationID = SourceTable.CMSDeclarationID ,
                        TargetTable.StatusCode = SourceTable.StatusCode ,
                        TargetTable.SubStatus = SourceTable.SubStatus ,
                        TargetTable.DepartmentCode = SourceTable.DepartmentCode ,
                        TargetTable.StartDate = SourceTable.StartDate ,
                        TargetTable.EndDate = SourceTable.EndDate ;
DBCC TRACEOFF(610)
END
Run Code Online (Sandbox Code Playgroud)

bri*_*ian 4

我的第一条评论是,您正在执行 ELT(提取、加载、转换)而不是 ETL(提取、转换、加载)。虽然 ELT 利用基于集合的关系优势并且速度非常快,但有时它们的写入密集度很高(存储困难)。具体来说,t 日志。这是因为转换是在磁盘上完成的(通常是更新或插入)。如果可能的话,我更喜欢 ETL,因为转换是在缓冲区中完成的,并且如果正确完成,则需要最少的 t-log 写入。缓冲液很便宜。快速存储则不然。对于某些批量操作,t-log 是一个非增值瓶颈。

以下是您正在做的一些事情,但我不推荐。

  1. 批量加载到 tempdb。我建议对目标数据库中的真实表进行批量加载。然后您可以相应地调整文件大小,而不必担心影响 tempdb。
  2. 将独立的程序捆绑在一起。将这两个过程分开。批量加载和合并是相互独立的。将它们分成单独的过程使它们更加模块化/单元可测试。

看来您已经很好地涵盖了最低限度的日志记录规则。您正在使用 tf 610(指定的排序键)在批量记录模式下加载到没有非集群的空 B 树。在临时表之外,这里一切看起来都很好。只要文件实际上是按键排序的,就应该没问题。您是在 tempdb 还是用户数据库上弹出日志?

关于合并语句:

更新将始终被完整记录。您是否正在更改表格的很大一部分?如果是这样,您可能会考虑在内存中进行合并(SSIS 数据流任务或.Net),然后批量加载到新表中。这是更多的工作,但大部分工作是在缓冲区中完成的,并且使用了最少的 t-log。如果更改部分很大,则最少记录的插入可能比完全记录的更新更快。

由于您使用的是 tf 610,因此在使用 tabblock 提示时插入可以最少地记录日志。有关与 tabblock 合并的更多信息,请参阅此处:链接注意,如果您采用此路线,更新仍将被完整记录。