当使用系统版本控制的时态表(SQL Server 2016 中的新功能)时,当使用此功能处理大型关系数据仓库中的缓慢变化维度时,查询创作和性能影响是什么?
例如,假设我有一个Customer带有Postal Code列的 100,000 行维度和一个Sales带有CustomerID外键列的数十亿行事实表。并假设我想查询“按客户邮政编码划分的 2014 年总销售额”。简化的 DDL 是这样的(为了清楚起见省略了很多列):
CREATE TABLE Customer
(
CustomerID int identity (1,1) NOT NULL PRIMARY KEY CLUSTERED,
PostalCode varchar(50) NOT NULL,
SysStartTime datetime2 GENERATED ALWAYS AS ROW START NOT NULL,
SysEndTime datetime2 GENERATED ALWAYS AS ROW END NOT NULL,
PERIOD FOR SYSTEM_TIME (SysStartTime, SysEndTime)
)
WITH (SYSTEM_VERSIONING = ON);
CREATE TABLE Sale
(
SaleId int identity(1,1) NOT NULL PRIMARY KEY CLUSTERED,
SaleDateTime …Run Code Online (Sandbox Code Playgroud) data-warehouse sql-server slowly-changing-dimension temporal-tables sql-server-2016
我正在尝试为包含数据的 SQL Server 2016 数据库中的某些现有表启用系统版本控制。我正在遵循Microsoft 的这些说明。
其中一张表如下所示:
CREATE TABLE [dbo].[ClientBeacon](
[ClientId] [int] NOT NULL,
[BeaconId] [int] NOT NULL,
[FromDate] [datetime] NOT NULL,
[ToDate] [datetime] NULL,
[Deleted] [bit] NOT NULL,
[ModifiedByUserId] [nvarchar](128) NOT NULL,
[ModifiedOn] [datetime] NOT NULL,
[Timestamp] [timestamp] NOT NULL,
CONSTRAINT [PK_ClientBeacon] PRIMARY KEY CLUSTERED
(
[ClientId] ASC,
[BeaconId] ASC
)
)
Run Code Online (Sandbox Code Playgroud)
我试图运行的脚本如下所示:
CREATE SCHEMA History;
GO
ALTER TABLE dbo.ClientBeacon
ADD
SysStartTime datetime2(0) GENERATED ALWAYS AS ROW START HIDDEN CONSTRAINT DF_ClientBeacon_SysStartTime DEFAULT SYSUTCDATETIME(),
SysEndTime datetime2(0) GENERATED ALWAYS …Run Code Online (Sandbox Code Playgroud) 我在访问时态表中的历史记录时遇到了一个奇怪的问题。通过 AS OF 子句访问临时表中较旧条目的查询比对最近历史条目的查询花费的时间更长。
历史表是由 SQL Server 生成的(包括日期列上的聚集索引并使用页面压缩),我向历史表添加了 5000 万行,我的查询检索了大约 25,000 行。
我试图确定问题的根本原因,但无法确定。到目前为止,我已经测试过:
我想多看看这个,但我也想确保我没有吠错树。首先,是否有其他人在访问时态表中的旧历史数据时遇到过同样的行为(我们只注意到速度超过 1000 万行)?其次,我可以使用哪些策略来进一步隔离性能问题的根本原因(我刚刚开始研究执行计划,但对我来说仍然有点神秘)?
这些是简单的检索查询:第一个访问较旧的行,第二个访问较新的行。
较旧的行~1200 毫秒执行时间
最近行~350ms 执行时间
不幸的是,我没有准备好访问 SQL 2016 实例来测试这个。如果我有一个具有以下架构的时态表:
create table blah (
foo int identity(1,1),
baa int
)
Run Code Online (Sandbox Code Playgroud)
我要运行以下语句:
update blah set baa = baa
Run Code Online (Sandbox Code Playgroud)
它会像我实际更新数据一样记录更改吗?
我已经查看了MSDN 文章中的临时表和修改数据的文章,但在那里找不到任何指定此内容的内容。
更新时态表中的一行时,该行的旧值存储在历史表中,事务开始时间为SysEndTime. 当前表中的新值将事务开始时间作为SysStartTime。
SysStartTime和SysEndTime是时datetime2态表用于记录某行何时是当前版本的列。事务开始时间是包含更新的事务开始的时间。
BOL 说:
系统 datetime2 列中记录的时间基于事务本身的开始时间。例如,在单个事务中插入的所有行都将在对应于 SYSTEM_TIME 周期开始的列中记录相同的 UTC 时间。
示例:我开始更新 Orders 表中的所有行,20160707 11:00:00事务运行需要 5 分钟。这会在历史记录表中为每一行创建一行SysEndTimeas 20160707 11:00:00。当前表中的所有行都有一个SysStartTimeof 20160707 11:00:00。
如果有人在20160707 11:01:00(更新正在运行时)执行查询,他们将看到旧值(假设默认读已提交隔离级别)。
但是,如果有人然后使用AS OF语法来查询时态表,因为20160707 11:01:00他们会看到新值,因为他们SysStartTime将是20160707 11:00:00.
对我来说,这意味着它不会像当时那样显示这些行。如果它使用事务结束时间,则问题将不存在。
问题:这是设计使然吗?我错过了什么吗?
我认为它使用事务开始时间的唯一原因是它是事务开始时唯一的“已知”。它不知道事务开始时何时结束,并且在结束时应用结束时间需要时间,这会使它应用的结束时间无效。这有意义吗?
这应该允许您重新创建问题。
似乎很难找到系统版本化时态表与旧选项(例如 DB 触发器和 CDC)之间的比较。我目前没有时间在 SQL Server 2016 上编写扩展测试,所以我想我会在这里问一下。
基本上,触发器的典型优势是它们在独立和集群/alwaysOn 环境中更易于管理,可以实时同步,并且可以访问会话数据,例如用户 ID。
另一方面,CDC 虽然需要更多的管理并且是异步的,但要轻得多,因此性能要好得多。因此,如果对触发器引起的瓶颈可能成为问题有任何疑问,CDC 基本上将是最佳解决方案。在硬件要求方面,由于使用日志和 cdc 审计表来跟踪更改,CDC 对额外空间的要求可以忽略不计。
问题:时态表与上述两个相比如何?在速度、性能、存储空间使用方面。何时应该使用时态表而不是触发器或 CDC?我什么时候不应该?
我理解任何可能复杂的事情,因为 DB 审计背后的业务需求和技术限制不会有一个简单的答案,因为它在很大程度上取决于项目的要求和范围。但是,如果能对上述问题有更多的了解,我们将不胜感激。谢谢!
2021年编辑:由于对此一直有一些兴趣,几年后我已经非常熟悉上述所有内容,这里是性能方面的简短摘要:我对 SQL Server 2016 的某个版本进行了测试,涉及插入、更新和删除10000 行来自 40 个不同类型的表一一列出,并绘制了每个表的总体时间、基本锁定信息等。简单的总结是,触发器平均为操作增加了 500-1000% 的延迟,而使用临时表和 CDC 时,每个操作的额外延迟接近 10%。如果我有确切的结果会有所帮助,但我不再记得它们了。触发过程非常简化,但每个更改的列插入一行,而 temporal / cdc 插入一行,而不管其中更改了多少列。在这个意义上,由于同时插入多行的键争用,一些更改可能使触发器看起来比它们更慢。然而,很明显触发器是最不适合简单审计的工具。所以这是我在创建这篇文章时试图理解的差异的简单技术概要:
触发器只有在你真的需要一些内置到数据库中的自定义逻辑时才有用,以监视 DML 更改,修改特定数据,在特定实例中捕获用户 ID 等。但尽量避免它们像瘟疫一样。他们的表现很糟糕。如果您需要审计或日志记录,它们是您应该查看的最后一个地方。
临时表一旦运行起来就非常容易管理,尤其是在像 Always On 这样的 HADR 中。由于它们支持压缩并反映从父表到历史表的大多数模式更改,因此它们需要很少的维护。特别是对于新的 SQL Server 版本,您可以设置保留期以删除超过 x 年的数据,因此存储和清理方面的考虑也可以忽略不计。它们就像事情来临时一样容易被遗忘,除非对需要更改数据的父表进行一些奇特的更新,在这种情况下,您必须取消链接,修改父表和历史记录表,然后再次链接它们。但这些都是罕见的,而且相对容易做到。时态表包很健壮,可以很好地处理错误,因此您会发现它很难被意外破坏。
然后,CDC 非常适用于报告服务或类似的场景,在这些场景中,您不介意异步数据,但您需要分析更改,例如每晚批处理。您可以将保留设置设置为仅保留 x 天的数据,以将存储成本降至最低。也就是说,根据我的经验,CDC 很挑剔而且不是很稳定。DML 有时会在没有警告的情况下“破坏”它,因此您可能需要数据库级 DDL 触发器来警告您 CDC 跟踪的对象发生了变化。您可能还需要为 HADR 设置自定义监视作业,因为它本身不处理故障转移事件。并且 CDC 有一个非常讨厌的倾向,即在被禁用后无法重新启动,这与使用 MS 自己的作业未正确更新它的状态有关。这意味着它偶尔需要手动工作以确保正确删除清理和捕获作业及其引用。也就是说,SSIS / RS 集成得非常好,使他们可以轻松使用 …
trigger sql-server change-data-capture temporal-tables sql-server-2016
想象一下,我有一个模式,其中包括Products,Orders,和OrderLineItems,与Products作为一个系统版本态表。
架构:
CREATE TABLE dbo.Products
(
ProductID INT NOT NULL IDENTITY PRIMARY KEY,
Name nvarchar(255) not null,
SysStart DATETIME2 (7) GENERATED ALWAYS AS ROW START NOT NULL,
SysEnd DATETIME2 (7) GENERATED ALWAYS AS ROW END NOT NULL,
PERIOD FOR SYSTEM_TIME ([SysStart], [SysEnd])
)
WITH (SYSTEM_VERSIONING = ON(HISTORY_TABLE = dbo.Products_History, DATA_CONSISTENCY_CHECK = ON));
GO
CREATE TABLE dbo.Orders
(
OrderID int not null identity primary key,
OrderDate datetime2 (7) not null
);
CREATE TABLE …Run Code Online (Sandbox Code Playgroud) 我有:
我正在使用以下语句使我的表成为临时表:
ALTER TABLE [dbo].[AnalysisCustomRollupsV2JoinGroups]
ADD [SysStartTime] DATETIME2(0) GENERATED ALWAYS AS ROW START HIDDEN CONSTRAINT DF_AnalysisCustomRollupsV2JoinGroups_SysStart DEFAULT GETUTCDATE()
,[SysEndTime] DATETIME2(0) GENERATED ALWAYS AS ROW END HIDDEN CONSTRAINT DF_AnalysisCustomRollupsV2JoinGroups_SysEnd DEFAULT CONVERT(DATETIME2(0), '9999-12-31 23:59:59'),
PERIOD FOR SYSTEM_TIME ([SysStartTime], [SysEndTime]);
ALTER TABLE [dbo].[AnalysisCustomRollupsV2JoinGroups]
SET (SYSTEM_VERSIONING = ON (HISTORY_TABLE = dbo.AnalysisCustomRollupsV2JoinGroupsChanges));
Run Code Online (Sandbox Code Playgroud)
问题:
在我的本地 SQL 实例上,我有很多数据库;查询在其中一些上成功运行非常奇怪,而在其中一些上它给了我以下错误:
消息 13542,级别 16,状态 0,第 51 行 ADD PERIOD FOR SYSTEM_TIME 在表 'dbo.AnalysisCustomRollupsV2JoinGroups' 上失败,因为有打开的记录,期间的开始时间设置为将来的某个值。
有时,当我调试/执行查询时,初始查询运行成功。
我读到,这可能是因为我在表中有现有数据。所以,我改变了这样的逻辑:
再说一次,在某些数据库上它是可以的,而在其他数据库上则不是。试图解决这个问题,我 …
我一直在建立一个具有 SQL Server 2017 后端的概念验证系统。
系统使用临时表来记录资产配置并跟踪随时间的变化。
我有一个链接到历史记录表的数据表,我们称之为 dbo.MSSQL_TemporaryHistoryFor_12345678900。
到现在为止还挺好。我有两个问题:
今天我关闭了表格上的版本控制,所以我可以添加一个计算列。这已完成并再次打开,没有错误。
现在我发现我无法查询更改之前的任何历史数据。新数据正在添加到历史记录中,但事先什么也没有。
查看 SSMS 内部,我现在可以看到有多个历史记录表,它们都具有相同的名称但带有十六进制后缀,例如 dbo.MSSQL_TemporaryHistoryFor_12345678900_A0B1C2D3。它们未链接到主数据表下方。它们只是在数据库中自行浮动。当我查询 sys.tables 时,这些没有显示为历史表,也没有链接到主数据表。
这些表确实包含缺失的历史数据。
因此,我的问题是:
这非常令人沮丧,因此我们将不胜感激地收到您能提供的任何帮助。谢谢。
我们正在考虑在应用程序中保存记录历史的不同方法。
部分负责人担心,这是SQL server比较新的特性,微软可能会停止支持。
这些担忧是否合理?
是否有此功能的路线图,说明 Microsoft 打算在未来使用此功能做什么?