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 SELF
到migrate_staging_data
程序中,但无济于事。我也试过改变ALTER QUEUE My_MigrationQueue
语句以包括EXECUTE AS SELF
基本上我想要做的就是每秒钟x
从 SQL Server 中执行一次我的迁移过程。在没有 Broker 的情况下,是否有更好的方法来每隔一段时间执行一个程序?
请注意:我不打算sa
在实际生产中使用登录名,我会在时机成熟时为数据库创建新的登录名。
我使用的是 SQL Server Enterprise x64,版本 12.0.4100.1
最简单(快速而肮脏的解决方案)但在安全方法方面存在风险是将数据库标记为TRUSTWORTHY
使用ALTER DATABASE [your_db_name] SET TRUSTWORTHY ON
.
请记住,将数据库标记为可信会将数据库提升dbo
为事实上的数据库sysadmin
您收到以下错误:
'服务器主体“sa”无法在当前安全上下文下访问数据库“Production”。
因为激活执行上下文仅在数据库中受信任,而不是在整个服务器中受信任。与整个服务器相关的任何内容(例如服务器级别视图或动态管理视图或链接服务器)的行为就像您以[公共]身份登录一样。
详细参考 Remus Rusanu 的博客文章:
此外,您应该有适当的流程来处理有毒消息,以防止禁用队列。
基本上我想做的就是每 x 秒从 SQL Server 中执行一次迁移过程。有没有更好的方法,无需 Broker 每隔一段时间执行一个过程?
您可以研究使用 SSIS从临时数据库到 PROD 数据库的增量加载等方法。加载数据时您也可以应用不同的技术。