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

问题描述 投票:5回答:10

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

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

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

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

这有什么问题?切换视图中间查询会导致问题吗?这可以吗?

感谢您的专业知识。

额外的信息

  • 例程是一个SSIS包,它执行许多步骤并最终截断/更新有问题的表
  • 阻塞进程是查询此表的另外两个存储过程。
sql sql-server sql-server-2005 views truncate
10个回答
7
投票

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

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


0
投票

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

希望这可以帮助,

法案


2
投票

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

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

如果你确实更新而不是替换表,那么你可以用另一种方式控制它 - 阅读用户不应该阻止表,并且可以通过乐观读取来逃避。

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


2
投票

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

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

我的0.02美元,

内特


2
投票

刚看过你正在使用SSIS

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

alt text (来源:sqlbi.eu

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


1
投票

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

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

如果您在事务中完成操作,则现有用户不会受到影响。

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


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表Prodcuts_WareHouse

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

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

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


1
投票

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

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

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

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

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

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


0
投票

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


0
投票

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

© www.soinside.com 2019 - 2024. All rights reserved.