在没有锁定表的情况下,在使用表时更新表中数据的最佳方法是什么?

时间:2009-05-20 14:38:32

标签: sql sql-server sql-server-2005 views truncate

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

......所以......

我们研究了我们的选择,并想出了一个缓解问题的想法。

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

    感谢您的专业知识。

    额外信息

    • 例程是一个SSIS包,它执行许多步骤并最终截断/更新有问题的表

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

10 个答案:

答案 0 :(得分:6)

您是否考虑过使用snapshot isolation。这将允许您为您的SSIS开始一个大的交易,仍然可以从表中读取。

此解决方案似乎比切换表更清晰。

答案 1 :(得分:2)

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

我看看没有截断表并重新填充它。这总是会干扰用户试图阅读它。

如果你确实更新而不是替换表,你可以用另一种方式控制它 - 阅读用户不应该阻止表,也许可以放弃乐观读取。

尝试将with(nolock)提示添加到读取SQL View语句中。即使桌子定期更新,您也应该能够获得大量用户阅读。

答案 2 :(得分:2)

刚看你正在使用SSIS

您可以使用来自http://www.sqlbi.eu/Home/tabid/36/ctl/Details/mid/374/ItemID/0/Default.aspx

的TableDiference组件

alt text
(来源:sqlbi.eu

这样你可以将更改应用到表中,一个接一个,但当然,这会慢得多,根据表的大小,服务器需要更多RAM,但锁定问题将完全纠正。

答案 3 :(得分:2)

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

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

我的0.02美元,

内特

答案 4 :(得分:1)

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

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

如果您在一项交易中完成了操作,那么现有用户将不会受到影响。

如何完成这将取决于表的大小以及数据的根本变化。如果您提供更多详细信息,我可以进一步提出建议。

答案 5 :(得分:1)

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

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

如果你必须在最终表格中进行“插入,更新和删除”

让我们假设结局表如下所示:

Table Products:
    ProductId       int
    QuantityOnHand  Int

您需要从仓库更新QuantityOnHand。

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

Table Prodcuts_WareHouse
    ProductId       int
    QuantityOnHand  Int

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

Table Prodcuts_Actions
    ProductId       int
    QuantityOnHand  Int
    Action          Char(1)

更新过程应该是这样的:

1.Truncate table Prodcuts_WareHouse

2.Truncate table 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

通过上述所有过程,您可以将要更新的记录数量最小化到必要的最小值,以及更新时锁定最终表格的时间。

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

答案 6 :(得分:1)

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

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

将数据导入“辅助”分区后,您可以立即切换“LIVE”分区OUT和“辅助”分区IN,从而实现零停机和无阻塞。

完成切换后,您可以在不影响新实时数据(以前是辅助分区)的用户的情况下截断不再需要的数据。

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

要了解有关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

答案 7 :(得分:0)

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

希望这有帮助,

比尔

答案 8 :(得分:0)

如果表格不是很大,您可以在短时间内将数据缓存在应用程序中。它可能不会完全消除阻塞,但它会降低更新发生时查询表的机会。

答案 9 :(得分:0)

我们在高使用率系统上执行此操作并且没有任何问题。但是,与所有数据库一样,确保它有用的唯一方法是在dev中进行更改然后加载测试它。不知道你的SSIS包没有,它可能仍然会造成阻塞。