并发,触发器,模式遗留,外键和性能

时间:2017-05-15 15:07:11

标签: sql sql-server tsql transactions azure-sql-database

想象一下,您开发的软件的架构需要为每个用户提供不同的数据库。这个数据库的模式充满了带有IDENTITY PK的表,以及其他充满FK的表引用这些表。

现在,在某个时刻,您需要将所有这些数据库折叠到一个大数据库中。您肯定改变了表的PK,删除了IDENTITY约束并将PK更改为ID + TenantID。

要解决ID问题,可以通过创建触发器来模拟IDENTITY。此触发器将覆盖INSERT语句(INSTEAD OF INSERT),并在事务上从“IdentityTable”读取用作ID的最后一个值,使用它来插入记录并更新“IdentityTable”中增加1的值。

除了并发之外,一切都很完美。如果有两个并发插入,则触发器是事务性的,但不是隔离的。这意味着N个执行的插入语句中只有一个会成功。

现在,我不是DBA,但如果我没有错,通过使用SERIALIZABLE事务级别,我会解决这个问题。我的查询肯定会更慢,但更安全。因此,只要我触发一个触发器,我就将隔离级别设置为SERIALIZABLE,并且我保证一旦触发完成,隔离级别就会被设置回原始值1

现在。我知道这个解决方案很臭。在我看来它很糟糕,因为我决定采用这种方式(当我不得不崩溃所有的数据库)。但应该做些什么呢? 我该如何解决碰撞的PK问题?我该怎么搬离这儿?你会做什么?

2 个答案:

答案 0 :(得分:2)

  

要解决ID问题,可以通过创建触发器来模拟IDENTITY

这是一个糟糕的主意。没有必要消除IDENTITY列只是因为键中有另一列。如果你真的想要,你也可以为每个顾客提供SEQUENCE。

例如

create table SomeTable
(
  CustomerID int,
  SomeTableID int identity,
  a int,
  b int,
  c int,
  constraint PK_SomeTable primary key (CustomerID,SomeTableID)
)
  

现在,我不是DBA,但如果我没有错,通过使用SERIALIZABLE交易级别,我会解决这个问题

你错了。 SERIALIZABLE无济于事,因为它仍允许并发读取。您需要在事务中读取ID表(触发器确保这一点),并在SELECT中使用UPDLOCK。这将在您读取之前对该行应用限制性锁定,并确保在当前会话更新之前,没有其他会话可以使用UPDLOCK读取该行。

  

你会做什么?

使用IDENTITY或SEQUENCE来生成密钥。

答案 1 :(得分:1)

因此,如果没有关于应用程序特定体系结构的大量知识,这真的不是一个可以回答的问题。我认为第一个错误是从所有这些列中删除了identity属性。没有必要这样做。您真正需要做的就是将租户ID添加到主键(然后通过相关表流动该更改)。但是假设有一个很好的理由在身份去除背后(为什么?)我认为一个序列本来是一个比“最大”方法更好的选择?

那我该怎么办?恢复标识列并删除触发器。从数据库的角度来看,你已经完成了。由于您现在在同一个数据库中托管多个租户,因此最好检查安全性和权限。然后说明如何提供将给定租户的信息恢复到之前某个时间点的能力。通常,当您进行此类更改时,客户端/报告方面需要完成大量工作。你试过跳过那部分吗?