SQL SERVER 2016 有一个新功能,Temporal table,他主要的功能是保留一份完整的数据表的变化记录,并允许通过这个表来进行数据的变更性分析。
启用CAMAIN 中temporal tables 会产生一个新的表在原表的名称前,增加后缀history。
主要的功能:
审核所有数据更改,并在必要时执行数据取证
与过去任何时候一样重建数据的状态
计算随时间的趋势
为决策支持应用程序维护一个缓慢变化的维度
从意外的数据更改和应用程序错误中恢复
我们下面来一个例子看一下 temporal 是怎么做的,我们在一个开启了 temporal 的表中进行 DML 操作,更改其中一行的数据。
可以看到历史表中的数据已经开始记录修改数据之前的所有这行的原始数据
然后我们在此更改数据
然后在查询历史表,这次还是一样记录更改前的记录状态
在插入数据的时候,会在原表中的字段,进行记录,而在历史表中并不会有任何记录,这点是要知道的。而更新记录,删除记录,都会对这些操作进行记录。同时还有一种MERGE 的方式也是将操作拆分成 DELETE ,UPDATE, INSERT 的方式来进行对应行的记录。
这么先进的东西,从2016开始的新功能,其实深究起来,也是有问题的。
上面的问题,在HOT table 中反应的比较多,在MICORSOFT 官方的 TECH在中,有提到,并且也有一些人提出了解决方法,当然微软并没有认为这一个BUG。
产生这个问题的原因是这样的,我们现在进行一个模拟,我们有两个SESSION A and B ,我们都要对其中一个表 CACONTRACT ,进行操作
而不幸的是, A 中的语句是这样写的。
UPDATE CACONTRACT SET NUM = 2 WHERE ID IN (select NUM FROM CAMAIN where num = 3)
看似没有问题,但我们可以将他看成一个事务,如果这样的处理时间是需要0.2毫秒, 但B SESSION 更快,例如他处理 UPDATE CACONTRACT SET BC = 2 WHERE ID = '000003DJKHJ'
看似两个操作其实不会影响,但B 操作由于快于 A 操作,则例如 ID 000003DJKHJ 在 表中的 SysStartTime 标记为 10点15分 .27997 微妙
而 A SESSION 在操作完毕后, 也需要在 sysstarttime 中写上我的处理
SysStartTime 进行一个更改的操作,将 SysStartTime 更改为 10:1527987 ,这已经明显不符合逻辑了,一个记录SysStartTime 再次更新要比当前的时间 要早,这在逻辑上走不通,所以,这个操作 A 就被forbidden. 尤其在特别热的表上。
光说不练假把式,来我们来模拟一下上述的情况吧
Follow me
1 在你的测试系统上建立一个测试表
REATE TABLE dbo.Orders
(
[OrderId] INT NOT NULL PRIMARY KEY CLUSTERED
, [OrderValue] DECIMAL(19,4)
, [ValidFrom] DATETIME2 (2) GENERATED ALWAYS AS ROW START
, [ValidTo] DATETIME2 (2) GENERATED ALWAYS AS ROW END
, PERIOD FOR SYSTEM_TIME (ValidFrom, ValidTo)
)
WITH (SYSTEM_VERSIONING = ON (HISTORY_TABLE = dbo.OrdersHistory));
GO
2 请允许以下脚本
BEGIN TRAN
WAITFOR DELAY '00:00:15';
UPDATE dbo.Orders
SET [OrderValue] = [OrderValue] + 1;
COMMIT TRAN
3 请在另一个 查询窗口执行如下语句
INSERT dbo.Orders ([OrderId], [OrderValue])
VALUES (1, 9.99), (2, 9.99);
GO
SELECT * FROM dbo.Orders;
GO
这就是插入数据在后,但却先插入,而要更新时在前,实际操作在后。这个时序性的系统自然就吃不消了。
解决这样的问题:
1 让关于报错表的 DML 操作足够的快,避免这样的事情发生(不过在很复杂的系统中,这很难)
2 在某些操作中,你想使用 holdlock 操作(其实是人为降低系统的处理性能)
3 在非常热的表中,停止使用这项微软的新功能,并等待微软能在新的版本中更新这个BUG。