5

我在一个经常使用的 SQL Server 2005 数据库中有一个表。它有我们的产品可用性信息。我们每小时都会从我们的仓库中获得更新,在过去的几年里,我们一直在运行一个例程来截断表格并更新信息。这只需要几秒钟,直到现在都不是问题。我们现在有更多的人使用我们的系统来查询这些信息,因此我们看到很多由于阻塞进程而导致的超时。

... 所以 ...

我们研究了我们的选择,并提出了减轻问题的想法。

  1. 我们会有两张桌子。表 A(活动)和表 B(非活动)。
  2. 我们将创建一个指向活动表(表 A)的视图。
  3. 所有需要此表信息(4 个对象)的东西现在都必须通过视图。
  4. 每小时例程会截断非活动表,用最新信息更新它,然后更新视图以指向非活动表,使其成为活动表。
  5. 该例程将确定哪个表处于活动状态并基本上在它们之间切换视图。

这有什么问题?在查询中切换视图会导致问题吗?这能行吗?

感谢您的专业知识。

额外的信息

  • 该例程是一个 SSIS 包,它执行许多步骤并最终截断/更新相关表

  • 阻塞进程是查询该表的另外两个存储过程。

4

10 回答 10

7

您是否考虑过使用快照隔离。它可以让你为你的 SSIS 东西开始一个大的交易,并且仍然从表中读取。

这个解决方案似乎比切换表格要干净得多。

于 2009-05-20T15:32:54.820 回答
2

我认为这是错误的方式 - 更新表必须锁定它,尽管您可以将锁定限制为每页甚至每行。

我会考虑不截断表格并重新填充它。这总是会干扰试图阅读它的用户。

如果您确实更新而不是替换表,您可以通过另一种方式控制它 - 阅读用户不应该阻塞表并且可能能够摆脱乐观读取。

尝试将 with(nolock) 提示添加到读取 SQL 视图语句中。即使表格定期更新,您也应该能够让大量用户阅读。

于 2009-05-20T14:45:17.770 回答
2

就个人而言,如果您总是要引入停机时间来对表运行批处理,我认为您应该在业务/数据访问层管理用户体验。引入一个表管理对象,该对象监视与该表的连接并控制批处理。

当新的批处理数据准备好时,管理对象停止所有新的查询请求(甚至可能排队?),允许现有查询完成,运行批处理,然后重新打开表进行查询。管理对象可以引发 UI 层可以解释的事件 (BatchProcessingEvent),以让人们知道该表当前不可用。

我的 0.02 美元,

内特

于 2009-05-20T14:50:56.057 回答
2

刚刚读到你正在使用 SSIS

您可以使用来自以下网址的 TableDiference 组件:http ://www.sqlbi.eu/Home/tabid/36/ctl/Det​​ails/mid/374/ItemID/0/Default.aspx

替代文字
(来源:sqlbi.eu

通过这种方式,您可以一个接一个地将更改应用于表,但是当然,这会慢得多,并且根据表的大小,服务器上需要更多的 RAM,但锁定问题将完全得到纠正。

于 2009-05-20T15:29:41.830 回答
1

为什么不使用事务来更新信息而不是截断操作。

截断未记录,因此无法在事务中完成。

如果您的操作是在事务中完成的,那么现有用户将不会受到影响。

如何做到这一点取决于表的大小以及数据变化的剧烈程度等因素。如果您提供更多详细信息,也许我可以提供更多建议。

于 2009-05-20T14:46:08.580 回答
1

一种可能的解决方案是尽量减少更新表所需的时间。

我将首先创建一个临时表以从仓库中下载数据。

如果您必须在决赛桌中进行“插入、更新和删除”

让我们假设决赛桌是这样的:

Table Products:
    ProductId       int
    QuantityOnHand  Int

您需要从仓库更新 QuantityOnHand。

首先创建一个临时表,如:

Table Prodcuts_WareHouse
    ProductId       int
    QuantityOnHand  Int

然后像这样创建一个“操作”表:

Table Prodcuts_Actions
    ProductId       int
    QuantityOnHand  Int
    Action          Char(1)

更新过程应该是这样的:

1.截断表Prodcuts_WareHouse

2.截断表Prodcuts_Actions

3.用仓库中的数据填充Prodcuts_WareHouse表

4. 用这个填充 Prodcuts_Actions 表:

插入:

INSERT INTO Prodcuts_Actions (ProductId, QuantityOnHand,Action)
SELECT     SRC.ProductId, SRC.QuantityOnHand, 'I' AS ACTION
FROM         Prodcuts_WareHouse AS SRC LEFT OUTER JOIN
                      Products AS DEST ON SRC.ProductId = DEST.ProductId
WHERE     (DEST.ProductId IS NULL)

删除

INSERT INTO Prodcuts_Actions (ProductId, QuantityOnHand,Action)
SELECT     DEST.ProductId, DEST.QuantityOnHand, 'D' AS Action
FROM         Prodcuts_WareHouse AS SRC RIGHT OUTER JOIN
                      Products AS DEST ON SRC.ProductId = DEST.ProductId
WHERE     (SRC.ProductId IS NULL)

更新

INSERT INTO Prodcuts_Actions (ProductId, QuantityOnHand,Action)
SELECT     SRC.ProductId, SRC.QuantityOnHand, 'U' AS Action
FROM         Prodcuts_WareHouse AS SRC INNER JOIN
                      Products AS DEST ON SRC.ProductId = DEST.ProductId AND SRC.QuantityOnHand <> DEST.QuantityOnHand

到目前为止,您还没有锁定决赛桌。

5.在事务中更新最终表:

BEGIN TRANS

DELETE Products FROM Products INNER JOIN
Prodcuts_Actions ON Products.ProductId = Prodcuts_Actions.ProductId
WHERE     (Prodcuts_Actions.Action = 'D')

INSERT INTO Prodcuts (ProductId, QuantityOnHand)
SELECT ProductId, QuantityOnHand FROM Prodcuts_Actions WHERE Action ='I';

UPDATE Products SET QuantityOnHand = SRC.QuantityOnHand 
FROM         Products INNER JOIN
Prodcuts_Actions AS SRC ON Products.ProductId = SRC.ProductId
WHERE     (SRC.Action = 'U')

COMMIT TRAN

通过上述所有过程,您可以将要更新的记录数量减至最少,从而缩短最终表在更新时被锁定的时间。

您甚至可以在最后一步不使用事务,因此在命令之间将释放表。

于 2009-05-20T15:13:41.363 回答
1

如果您可以使用 SQL Server 企业版,那么我建议您使用 SQL Server 分区技术。

您可以将当前所需的数据保存在“实时”分区中,并将更新版本的数据保存在“辅助”分区中(不可用于查询,而是用于管理数据)。

将数据导入“辅助”分区后,您可以立即切换“实时”分区输出和“辅助”分区输入,从而导致零停机时间且无阻塞。

完成切换后,您可以截断不再需要的数据,而不会影响新活动数据(以前的辅助分区)的用户。

每次您需要执行导入作业时,您只需重复/反转该过程。

要了解有关 SQL Server 分区的更多信息,请参阅:

http://msdn.microsoft.com/en-us/library/ms345146(SQL.90).aspx

或者你可以问我:-)

编辑:

附带说明一下,为了解决任何阻塞问题,您可以使用 SQL Server 行版本控制技术。

http://msdn.microsoft.com/en-us/library/ms345124(SQL.90).aspx

于 2009-05-20T15:17:06.550 回答
0

我们在我们的高使用率系统上执行此操作,并且没有遇到任何问题。但是,与所有事物数据库一样,确保它会有所帮助的唯一方法是在 dev 中进行更改,然后对其进行负载测试。不知道您的 SSIS 包还有其他用途,它仍然可能导致阻塞。

于 2009-05-20T15:13:22.893 回答
0

如果表不是很大,您可以在应用程序中缓存数据一小段时间。它可能不会完全消除阻塞,但会减少发生更新时查询表的机会。

于 2009-05-20T15:46:43.887 回答
0

也许对阻塞的进程进行一些分析是有意义的,因为它们似乎是你的环境中已经改变的一部分。只需要一个写得不好的查询来创建您所看到的块。除非查询写得不好,否则该表可能需要一个或多个覆盖索引来加速这些查询并让您重新开始工作,而无需重新设计您已经工作的代码。

希望这可以帮助,

账单

于 2009-05-20T17:10:56.180 回答