sa 没有通过与 Service Broker 同义词访问其他数据库的权限

Bra*_*don 5 sql-server permissions service-broker sql-server-2014

我一直在尝试让这个 Service Broker 和计时器的东西连续工作一个多星期,所以如果这看起来像……呃……愤怒,请耐心等待。在 SQL Server 方面,我也相当缺乏经验。

我已经建立了两个数据库来处理非常大量的数据。第一个数据库用于暂存,表没有参照完整性,我的应用程序将重磅这些表只是为了将数据放入数据库中。这很好用。第二个数据库用于生产就绪数据,具有完整性约束等......我已经设置了指向Production的表的同义词Staging

我已经设置了一个 Service Broker 计时器队列/服务,每隔几秒钟执行一次,以将数据从已完成的临时表迁移到生产中。其 SQL 如下:

IF EXISTS (SELECT * FROM sys.services WHERE name = 'My_MigrationService')
BEGIN 
    DROP SERVICE My_MigrationService
END 
GO

IF EXISTS (SELECT * FROM sys.service_queues WHERE name = 'My_MigrationQueue')
BEGIN 
    DROP QUEUE My_MigrationQueue
END 
GO

CREATE QUEUE My_MigrationQueue
GO

CREATE SERVICE My_MigrationService ON QUEUE My_MigrationQueue ([DEFAULT])
GO

ALTER QUEUE My_MigrationQueue 
WITH ACTIVATION (
    STATUS = ON
    , MAX_QUEUE_READERS = 1
    , EXECUTE AS OWNER
    , PROCEDURE_NAME = migration_handler
);
GO


IF EXISTS (SELECT * FROM sys.procedures WHERE name = 'restart_migration_conversation')
BEGIN 
    DROP PROCEDURE [dbo].[restart_migration_conversation]
END 
GO


CREATE PROCEDURE restart_migration_conversation
AS 
BEGIN 
    -- SET NOCOUNT ON added to prevent extra result sets from
    -- interfering with SELECT statements.
    SET NOCOUNT ON;

    DECLARE @conversationHandle UNIQUEIDENTIFIER = (SELECT TOP 1 [conversation_handle] 
                                                    FROM sys.conversation_endpoints
                                                    WHERE [far_service] = 'My_MigrationService')

    IF @conversationHandle IS NOT NULL
    BEGIN 
        END CONVERSATION (@conversationHandle)
    END 

    BEGIN DIALOG CONVERSATION @conversationHandle
        FROM SERVICE [My_MigrationService]
        TO SERVICE N'My_MigrationService', N'CURRENT DATABASE'
        WITH ENCRYPTION = OFF;

    BEGIN CONVERSATION TIMER (@conversationHandle) TIMEOUT = 1;

END
GO



CREATE PROCEDURE migration_handler
AS 
BEGIN 
    -- SET NOCOUNT ON added to prevent extra result sets from
    -- interfering with SELECT statements.
    SET NOCOUNT On;
    DECLARE @messageType        SYSNAME
    DECLARE @conversationHandle UNIQUEIDENTIFIER
    BEGIN TRANSACTION;
        RECEIVE TOP (1)
            @messageType = [message_type_name]
            , @conversationHandle = [conversation_handle]
        FROM My_MigrationQueue

        IF @conversationHandle IS NOT NULL AND @messageType = N'http://schemas.microsoft.com/SQL/ServiceBroker/DialogTimer'
        BEGIN
            EXEC migrate_staging_data

            BEGIN CONVERSATION TIMER (@conversationHandle) TIMEOUT = 2;
        END

    COMMIT TRANSACTION;
END
GO
Run Code Online (Sandbox Code Playgroud)

该过程migrate_staging_data是包含对Production数据库的同义词用法的过程。我将其排除在外,因为它非常大,并且生成MCVE将非常耗时,但收益不大。

队列正确执行,准时等......现在,当我migrate_staging_data使用 SSMS执行时,一切都按计划进行,数据迁移正确,一切都很好。一旦我使用 Service Broker 计时器执行它,我就会在我的 SQL Server 日志中得到一个条目,上面写着

在队列 'Staging.dbo.My_MigrationQueue' 上运行的激活过程 '[dbo].[migration_handler]' 输出以下内容: '服务器主体 "sa" 在当前安全上下文下无法访问数据库 "Production"。

随后尝试通过 Broker 调用migration_handler输出

在队列“Staging.dbo.My_MigrationQueue”上运行的激活过程“[dbo].[migration_handler]”输出以下内容:“服务队列“My_MigrationQueue”当前已禁用。

为什么?

为什么不像sa从 SSMS 或其他上下文执行时那样在 Broker 下拥有权限?我怎样才能sa允许这样做?

我已经尝试根据一些研究添加WITH EXECUTE AS SELFmigrate_staging_data程序中,但无济于事。我也试过改变ALTER QUEUE My_MigrationQueue语句以包括EXECUTE AS SELF

基本上我想要做的就是每秒钟x从 SQL Server 中执行一次我的迁移过程。在没有 Broker 的情况下,是否有更好的方法来每隔一段时间执行一个程序?

请注意:我不打算sa在实际生产中使用登录名,我会在时机成熟时为数据库创建新的登录名。

我使用的是 SQL Server Enterprise x64,版本 12.0.4100.1

Kin*_*hah 3

最简单(快速而肮脏的解决方案)但在安全方法方面存在风险是将数据库标记为TRUSTWORTHY使用ALTER DATABASE [your_db_name] SET TRUSTWORTHY ON.

请记住,将数据库标记为可信会将数据库提升dbo为事实上的数据库sysadmin

您收到以下错误:

'服务器主体“sa”无法在当前安全上下文下访问数据库“Production”。

因为激活执行上下文仅在数据库中受信任,而不是在整个服务器中受信任。与整个服务器相关的任何内容(例如服务器级别视图或动态管理视图或链接服务器)的行为就像您以[公共]身份登录一样。

详细参考 Remus Rusanu 的博客文章:

此外,您应该有适当的流程来处理有毒消息,以防止禁用队列。

基本上我想做的就是每 x 秒从 SQL Server 中执行一次迁移过程。有没有更好的方法,无需 Broker 每隔一段时间执行一个过程?

您可以研究使用 SSIS从临时数据库到 PROD 数据库的增量加载等方法。加载数据时您也可以应用不同的技术。