Canned answer:
在很多情况下,索引视图可能会解决您的短期性能目标,但在以后的某个时间会适得其反。所以如果你选择使用索引视图,你可能需要一个退出策略。让我描述一下索引视图的一些常见问题。
索引视图可能会增加锁定争用。
很容易演示。创建下表:
CREATE TABLE dbo.ChildTable(ChildID INT NOT NULL
CONSTRAINT PK_ChildTable PRIMARY KEY,
ParentID INT NOT NULL,
Amount INT NOT NULL);
GO
从 SSMS 中的一个选项卡,运行此脚本:
BEGIN TRAN;
INSERT INTO dbo.ChildTable(ChildID, ParentID, Amount)
VALUES(1,1,1);
从另一个选项卡,运行一个类似的:
BEGIN TRAN;
INSERT INTO dbo.ChildTable(ChildID, ParentID, Amount)
VALUES(2,1,1);
ROLLBACK;
请注意,两个插入都已完成,它们不会相互阻塞。在两个选项卡中回滚,并创建索引视图:
CREATE VIEW dbo.ChildTableTotals WITH SCHEMABINDING
AS
SELECT ParentID,
COUNT_BIG(*) AS ChildRowsPerParent,
SUM(Amount) AS SumAmount
FROM dbo.ChildTable
GROUP BY ParentID;
GO
CREATE UNIQUE CLUSTERED INDEX ChildTableTotals_CI
ON dbo.ChildTableTotals(ParentID);
重新运行两个插入。注意第二个没有完成;它被阻止了。原因很简单:第一个insert修改了indexed view中对应的entry,所以insert获取并持有了锁。
同样容易证明,当您创建索引视图时,死锁也可能变得更容易。
注意:这不是索引视图实现方式的问题。如果您推出自己的汇总表,并开发直接修改它以使其保持最新的触发器,您将遇到同样的问题。只有当你不一直维护你的汇总表时,你才能解决这个锁定问题,但是对此更详细的讨论超出了本文的范围。
编辑:这个例子在你看来可能是做作的,但它所展示的问题是非常真实和非常普遍的。 OLTP 环境中的索引视图用途有限,因为它们严重增加了锁争用并导致许多死锁。有人在 OLTP 中创建它们但最终放弃是很常见的,因为它们引入的问题多于解决的问题。
有两种常见的方式来演示并发引起的问题 - 我们要么编写循环并从多个连接运行它们,要么显式地在两个或多个连接中开始事务。我鼓励大家想出一个更简单的方法来演示这个问题。