SQL Azure替代Service Broker

时间:2019-07-01 09:51:09

标签: sql-server logging azure-sql-database audit service-broker

我们的软件是连接到SQL数据库的Windows应用程序的集合。当前,我们所有的客户端站点都有自己的服务器和SQL Server数据库,但是我也在努力使我们的软件也可以与Azure托管的数据库一起使用。

我碰到了一个障碍,到目前为止,在谷歌搜索中没有发现任何特别有用的东西。

当前的SQL Server版本包括我编写的数据库审核系统,该系统执行以下操作:-

C#应用程序在连接字符串中包含有关它是哪个程序和版本以及当前登录的用户的信息。

重要表具有更新和删除触发器,这些触发器将所有更改的详细信息发送到Service Broker队列。 (我不记录插入内容)。

然后,Service Broker遍历队列,并将更改的详细信息记录到单独的AuditLog表中。

这些详细信息包括:-

表,已更改的行PK,字段,旧值,新值,是更新还是删除,更改日期/时间,登录到我们软件的用户的UserID,以及由哪个程序和版本构成改变。

这一切都很好,我希望将系统保持为Azure版本的原样,但是不幸的是SQL Azure没有Service Broker。

因此,我需要寻找一种替代方法,正如我提到的那样,这很麻烦。

有SQL Azure托管实例,它确实具有Service Broker,但是对于我们来说,它们甚至太昂贵了。我们的客户中没有一个愿意每月支付那么多钱。

我看过的其他东西似乎都没有我需要的一切。特别是,记录哪个程序,版本和UserID。请注意,这不是SQL登录用户ID,每个用户都将是相同的,这是用户登录到我们的软件时在Users表中获得的ID,并在连接字符串中传递。

因此,理想情况下,我想要与我拥有的东西类似的东西,只是用其他东西代替Service Broker:-

C#应用程序在连接字符串中包含有关它是哪个程序和版本以及当前登录的用户的信息。

重要表具有“更新”和“删除”触发器,这些触发器将所有更改的详细信息发送到某种异步队列

然后,某些事情通过队列正常程序流之外,并将更改的详细信息记录到单独的AuditLog表中。

异步队列和正常程序流之外的处理很重要。显然,我可以很轻松地让Update和Delete触发器完成所有处理并将记录添加到AuditLog表中,实际上这是系统的v1.0,但是问题在于SQL会等到触发器完成之前返回到C#程序。然后,当发生多个更新或删除操作时,这会使C#程序的运行速度大大降低。

我很乐于研究其他日志记录系统,而不是上面的系统,但是只记录数据更改而没有传递我额外信息的东西,特别是程序,版本和UserID,对我没有任何用处。我们的用户在查询任何认为不正确的更改时总是希望知道此信息。

那么,对SQL Azure Service Broker的替代方案有何建议? TIA!

1 个答案:

答案 0 :(得分:0)

好吧,看来我有一个潜在的解决方案:临时表

临时表在Azure中工作,并在发生任何更改时在历史记录表中记录新行:-

CREATE TABLE dbo.LMSTemporalTest   
(    
  [EmployeeID] INT NOT NULL PRIMARY KEY CLUSTERED   
  , [Name] NVARCHAR(100) NOT NULL  
  , [Position] NVARCHAR(100) NOT NULL   
  , [Department] NVARCHAR(100) NOT NULL  
  , [Address] NVARCHAR(1024) NOT NULL  
  , [AnnualSalary] DECIMAL (10,2) NOT NULL  
  , [UpdatedBy] UniqueIdentifier NOT NULL
  , [UpdatedDate] DateTime NOT NULL
  , [ValidFrom] DateTime2 (2) GENERATED ALWAYS AS ROW START HIDDEN
  , [ValidTo] DateTime2 (2) GENERATED ALWAYS AS ROW END HIDDEN
  , PERIOD FOR SYSTEM_TIME (ValidFrom, ValidTo)  
)    
WITH (SYSTEM_VERSIONING = ON (HISTORY_TABLE = dbo.LMSTemporalTestHistory));  
GO

然后我可以在表中插入一条记录...

INSERT INTO LMSTemporalTest(EmployeeID,Name,Position,Department,Address,AnnualSalary, UpdatedBy, UpdatedDate)
VALUES(1, 'Bob', 'Builder', 'Fixers','Oops I forgot', 1, '0D7F5584-C79B-4044-87BD-034A770C4985', GetDate())
GO

更新行...

UPDATE LMSTemporalTest SET 
Address = 'Sunflower Valley, Bobsville',
UpdatedBy = '2C62290B-61A9-4B75-AACF-02B7A5EBFB80',
UpdatedDate = GetDate()
WHERE EmployeeID = 1
GO

再次更新该行...

UPDATE LMSTemporalTest SET 
AnnualSalary = 420.69,
UpdatedBy = '47F25135-35ED-4855-8050-046CD73E5A7D',
UpdatedDate = GetDate()WHERE EmployeeID = 1
GO

然后检查结果:-

SELECT * FROM LMSTemporalTest
GO

EmployeeID  Name    Position    Department  Address AnnualSalary    UpdatedBy   UpdatedDate
1   Bob Builder Fixers  Sunflower Valley, Bobsville 420.69  47F25135-35ED-4855-8050-046CD73E5A7D    2019-07-01 16:20:00.230

注意:由于我将它们设置为“隐藏”,因此“有效期自”和“有效期至”不会显示

检查日期/时间范围的更改:-

SELECT * FROM LMSTemporalTest  
FOR SYSTEM_TIME BETWEEN '2019-Jul-01 14:00' AND '2019-Jul-01 17:10'   
WHERE EmployeeID = 1
ORDER BY ValidFrom;
GO

EmployeeID  Name    Position    Department  Address AnnualSalary    UpdatedBy   UpdatedDate
1   Bob Builder Fixers  Oops I forgot   1.00    0D7F5584-C79B-4044-87BD-034A770C4985    2019-07-01 16:20:00.163
1   Bob Builder Fixers  Sunflower Valley, Bobsville 1.00    2C62290B-61A9-4B75-AACF-02B7A5EBFB80    2019-07-01 16:20:00.197
1   Bob Builder Fixers  Sunflower Valley, Bobsville 420.69  47F25135-35ED-4855-8050-046CD73E5A7D    2019-07-01 16:20:00.230

我什至可以查看历史记录表

SELECT * FROM LMSTemporalTestHistory
GO

EmployeeID  Name    Position    Department  Address AnnualSalary    UpdatedBy   UpdatedDate ValidFrom   ValidTo
1   Bob Builder Fixers  Oops I forgot   1.00    0D7F5584-C79B-4044-87BD-034A770C4985    2019-07-01 16:20:00.163 2019-07-01 16:20:00.16  2019-07-01 16:20:00.19
1   Bob Builder Fixers  Sunflower Valley, Bobsville 1.00    2C62290B-61A9-4B75-AACF-02B7A5EBFB80    2019-07-01 16:20:00.197 2019-07-01 16:20:00.19  2019-07-01 16:20:00.22

注意:当前行仍然有效,因此不会显示

我们所有重要的表都已经具有CreatedBy,CreatedDate,UpdatedBy和UpdatedDate,因此我可以将它们用于UserID日志记录。没有明显的方法可以作为标准处理程序和版本,但是我总是可以添加另一个隐藏字段并使用触发器进行设置。

编辑:实际测试过

第一个障碍是:您实际上可以将现有表更改为临时表吗?答案是:是!

ALTER TABLE Clients ADD 
    [ValidFrom] DateTime2 (2) GENERATED ALWAYS AS ROW START HIDDEN NOT NULL DEFAULT '1753-01-01 00:00:00.000',
    [ValidTo] DateTime2 (2) GENERATED ALWAYS AS ROW END HIDDEN NOT NULL DEFAULT '9999-12-31 23:59:59.997',
    PERIOD FOR SYSTEM_TIME (ValidFrom, ValidTo)  
GO

ALTER TABLE Clients SET (SYSTEM_VERSIONING = ON (HISTORY_TABLE = dbo.ClientsHistory))
GO

上面重要的一点是ValidFrom和ValidTo字段的默认设置。仅当ValidTo是DateTime2的最大值时才起作用,因此为“ 9999-12-31 23:59:59.997”。 ValidFrom似乎无关紧要,所以我将其设置为最低限度只是为了覆盖所有内容。

好,所以我已经转换了一个表,但是它现在有两个非Azure表没有的额外字段,这些字段在理论上是隐藏的,但是我们的软件会抱怨吗?

似乎没有。启动该软件,在“客户”表上编辑记录并保存,该软件完全没有抱怨。

检查了Clients和ClientsHistory表:-

SELECT * FROM Clients  
FOR SYSTEM_TIME BETWEEN '1753-01-01 00:00:00.000' AND '9999-12-31 23:59:59.997'   
WHERE sCAccountNo = '0001064'
ORDER BY ValidFrom

显示两个记录,原始记录和编辑记录,并且现有的UpdatedUser和UpdatedDate字段正确显示,因此我知道是谁进行更改以及何时进行的。

SELECT * FROM ClientsHistory

显示原始记录,并将ValidTo设置为更改日期,

一切似乎都很好,现在我只需要检查它是否仍然仅将查询中的当前记录返回到我们的软件即可:-

SELECT * FROM Clients  
WHERE sCAccountNo = '0001064'

仅返回了一条记录,并且没有显示HIDDEN字段ValidFrom和ValidTo。

在我们的软件中搜索了客户端0001064,它再次返回了一条记录,并且没有抱怨另外两个字段。

仍然需要设置一些触发器,并添加另一个HIDDEN字段来记录连接字符串中的程序和版本,但是看起来Temporal Tables给了我一个可行的审核选项。

到目前为止,唯一的缺点是它会为每组更改创建一个完整的记录行,这意味着您必须将其与其他记录进行比较以找出更改的内容,但我可以编写一些内容来简化此操作。