Sql server 如何在ETL管道中正确截断临时表?

Sql server 如何在ETL管道中正确截断临时表?,sql-server,locking,azure-sql-database,etl,staging-table,Sql Server,Locking,Azure Sql Database,Etl,Staging Table,我们有一个ETL管道,为上传到存储帐户(Azure)的每个CSV运行。它在CSV上运行一些转换,并将输出写入另一个位置(也作为CSV),并调用数据库(SQL Azure)上的存储过程,该存储过程将生成的CSV摄取(大容量插入)到暂存表中 此管道可以同时执行,因为多个资源可以将文件上载到存储器。因此,临时表经常插入数据 然后,我们有一个计划的SQL作业(弹性作业),它触发一个SP,该SP将数据从临时表移动到最终表中。 此时,我们希望截断/清空暂存表,这样就不会在下一次执行作业时重新插入它们 问题是

我们有一个ETL管道,为上传到存储帐户(Azure)的每个CSV运行。它在CSV上运行一些转换,并将输出写入另一个位置(也作为CSV),并调用数据库(SQL Azure)上的存储过程,该存储过程将生成的CSV摄取(大容量插入)到暂存表中

此管道可以同时执行,因为多个资源可以将文件上载到存储器。因此,临时表经常插入数据

然后,我们有一个计划的SQL作业(弹性作业),它触发一个SP,该SP将数据从临时表移动到最终表中。 此时,我们希望截断/清空暂存表,这样就不会在下一次执行作业时重新插入它们

问题是,我们无法确定在从暂存表加载到最终表和truncate命令之间,没有任何写入暂存表的新数据可以在不首先插入最终表的情况下被截断

当我们将数据复制到最终表中时,是否有一种方法可以锁定暂存表,以便尝试写入的SP(从ETL管道调用)将只等待锁释放?这可以通过使用事务或一些手动锁定命令来实现吗

如果没有,处理这个问题的最佳方法是什么

当我们将数据复制到最终表中时,是否有一种方法可以锁定暂存表,以便尝试写入的SP(从ETL管道调用)将只等待锁释放?这可以通过使用事务或一些手动锁定命令来实现吗

看起来您正在搜索一种比事务级别更广泛的机制。SQL Server/Azure SQL DB有一个应用程序锁,称为应用程序锁

锁定应用程序资源

放置在资源上的锁与当前事务或当前会话相关联。当事务提交或回滚时,将释放与当前事务关联的锁。当会话注销时,将释放与会话关联的锁。当服务器因任何原因关闭时,所有锁都会释放。

可以使用sp_releaseapplock显式释放锁。当应用程序为同一个锁资源多次调用sp_getapplock时,必须调用相同次数的sp_releaseapplock才能释放锁。当使用事务锁所有者打开锁时,在提交或回滚事务时释放该锁

这基本上意味着您的ETL工具应该向DB打开单个会话,获取锁并在完成时释放。在尝试执行任何操作之前,其他会话应该尝试获取锁(它们无法获取锁,因为锁已经被占用),等待它释放后继续工作。

我喜欢并在少数地方使用此方法,因为它具有灵活性,并且您可以完全控制锁逻辑和等待时间

我看到的唯一问题是,在您的情况下,并发进程并不都是相等的

SP1将数据从临时表移动到主表中。您的系统从未尝试运行此SP的多个实例

另一个向暂存表插入数据的SP2可以同时运行几次,这样做很好

很容易实现可以防止SP1或SP2组合的任何并发运行的锁定。换句话说,如果SP1和SP2的锁定逻辑相同,并且它们被同等对待,那么就很容易了。但是,您不能同时运行多个SP2实例

如何实现阻止SP1和SP2并发运行的锁定,同时允许多个SP2实例同时运行,这一点并不明显


还有另一种方法不试图阻止SP的并发运行,但包含并期望可以同时运行

一种方法是将
IDENTITY
列添加到staging表中。或者自动填充datetime,如果您可以保证它是唯一的,并且不会减少,这可能会很棘手。或列

SP2内部将数据插入临时表的逻辑不会改变

SP1内部将数据从暂存表移动到主表的逻辑需要使用这些标识值


首先从staging表中读取当前标识的最大值,并将其存储在变量中,例如,
@MaxID
。在SP1中,所有后续从暂存表中选择、更新和删除操作都应该包括一个过滤器
,其中IDI将使用两个相同的暂存表提出解决方案。让我们把它们命名为StageLoading和StageProcessing。
加载过程将包含以下步骤:
1.开始时,两个表都是空的。
2.我们将一些数据加载到StageLoading表中(我假设每个加载都是一个事务)。
3.弹性作业开始时,它可以:
-更改表格开关,将所有数据从StageLoading移动到StageProcessing。它将使StageLoading为空,并为下一次加载做好准备。这是一个元数据操作,因此需要毫秒,并且完全阻塞,因此将在加载之间执行。
-将数据从阶段处理加载到最终表格。
-截断表阶段处理。
4.现在我们为下一个弹性工作做好了准备


如果我们在StageProcessing不为空时尝试切换,ALTER将失败,这将意味着最后一个加载进程失败。

假设您有一个出站作业

  • 将OutboundProcessing位默认值0添加到表中
  • 在作业中,设置OutboundProcessing=1,其中OutboundProcessing=0(声明行)
  • 对于ETL,将WHERE OutboundProcessing=1合并到数据源的查询中(传输行)
  • 在ETL之后,DEL
    CREATE PROCEDURE SP2  -- or SP1
    AS
    BEGIN
        SET NOCOUNT ON;
        SET XACT_ABORT ON;
    
        BEGIN TRANSACTION;
        BEGIN TRY
            -- Maximum number of retries
            DECLARE @VarCount int = 10;
    
            WHILE (@VarCount > 0)
            BEGIN
                SET @VarCount = @VarCount - 1;
    
                DECLARE @VarLockResult int;
                EXEC @VarLockResult = sp_getapplock
                    @Resource = 'StagingTable_app_lock',
                    -- this resource name should be the same in SP1 and SP2
                    @LockMode = 'Exclusive',
                    @LockOwner = 'Transaction',
                    @LockTimeout = 60000,
                    -- I'd set this timeout to be about twice the time
                    -- you expect SP to run normally
                    @DbPrincipal = 'public';
    
                IF @VarLockResult >= 0
                BEGIN
                    -- Acquired the lock
    
                    -- for SP2
                    -- INSERT INTO StagingTable ...
    
                    -- for SP1
                    -- SELECT FROM StagingTable ...
                    -- TRUNCATE StagingTable ...
    
                    -- don't retry any more
                    BREAK;
                END ELSE BEGIN
                    -- wait for 5 seconds and retry
                    WAITFOR DELAY '00:00:05';
                END;
            END;
    
            COMMIT TRANSACTION;
        END TRY
        BEGIN CATCH
            ROLLBACK TRANSACTION;
            -- log error
        END CATCH;
    
    END