2

我有两个应用程序。一个是主要应用程序,其中存在从一个表到另一个表的按需同步(不要问我为什么)。其他应用程序将数据从同步表填充到主表。

当其他应用程序正在运行并填写表格时,我需要在主应用程序中知道该进程正在运行并禁止在主应用程序中按需同步。我正在考虑制作一些表并在一个事务中同步锁定它。完成填充数据后释放锁。

我对如何在 delphi 中执行此操作、如何在主应用程序中同步设置事务感兴趣?如何检查表是否被锁定?目标也是同步应用程序是否停止释放锁定。

谢谢

4

2 回答 2

10

Firebird 是版本控制引擎,并且在那里锁定是一件不自然的事情。引擎、库——它们都经过优化以避免锁定。我建议你退后几步,大范围地看这张照片。您最好根据应用程序行为来制定您的任务,然后考虑如何修改它们对数据库的行为以获得您需要的东西。

请!给应用程序一些名称。很难用“这个应用程序”和“其他应用程序”来思考和计划——你不可避免地会开始混合它们。

在 Firebird 中,您可以锁定单行,因此要锁定表,您应该创建一个由单行组成的表,但即使这种行为也会有问题,并且只能由尝试更改相同记录和更改的其他应用程序检查commit。OldSchool 变体看起来像UPDATE FlagTable SET FlagColumn = FlagColumn + 0 /* WHERE ID = ... */现代版本引入的SELECT .... FOR UPDATE WITH LOCK声明

然而,让我再次强调一下——即使是 Firebird 中的每行锁定也是一种不自然和奇异的条件,具有非常特定的创建和检查方式。只能通过滥用此功能并锁定表中的每一行来实现每表行,这可能会使服务器陷入瘫痪,试图强制其进入它旨在不惜一切代价避免的模式。

如果没有关于您的应用程序以及您对它们的权力以及数据流的更多详细信息,我们几乎无法提出最佳建议。但是,我可以建议您探索几条路线。

_1。只需通过单个事务插入。

如果插入应用程序直到commit transaction最后一个数据行被插入,那么同步读取应用程序就不会看到该数据。它是未提交的——因此是不可见的。

这是基于所有真实 SQL 服务器基于事务的性质的最自然的方法。但它有一个缺点:如果插入过程很慢(例如你每分钟只能计算一行,需要计算并插入100行),会持有很长的未提交事务,这会对垃圾收集造成不良影响和服务器性能。但这是最简单的尝试和查看的方法。

_2。使用全局临时表。

您的插入应用程序使用每个连接的 GTT(基本:它应该是每个连接,而不是每个事务!)并根据需要填充它,如果它觉得这有用,则将长过程拆分为数字事务。它不接触主表,因此同步阅读应用程序可以尽可能多地保持同步。

在每个连接的 GTT 填充数据后,启动一个“导出”事务,该事务在单个操作后仓促提交:INSERT INTO MAIN-TABLE SELECT * FROM GTT.

  • 如果这是您真正需要的,还有实现 INSERT-OR-UPDATE 功能的SQL MERGE命令。

在这个单一操作之后,您提交导出事务并断开与数据库的连接。这种断开连接将以最有效的方式从每个连接的 GTT 中刷新所有数据。如果您需要继续工作,您可以稍后重新连接。

就像上面一样,这个策略似乎不会影响同步读取应用程序的工作流程,只需要插入应用程序的良好实施。

_3。真正的锁定方法需要一个复杂的解决方案,同时修改两个应用程序。

  1. 您实现了特殊的“更新程序”表,该表为每个活动的 inserting-applicationconnectionID列出(或少数几个)。
  2. 您修改syncing-reading-application以便它侦听 Firebird 事件
  3. 您修改同步读取应用程序,使其具有内部“锁定”模式,并自愿避免读取数据库,除了罕见的保持活动请求,如SELECT 1 FROM RDB$DATABASE每分钟一次。
  4. 您修改syncing-reading-application以便在连接后它以模式读取“updaters”表READ COMMITED并进入“锁定”模式,除非表为空
  5. 您修改syncing -reading-application以便在接收到 UPDATERS_CHANGED 事件时,它立即进入“锁定”模式,然后等待几秒钟,然后读取“updaters”表以查看它是否为空,它可以退出“锁定”模式。
  6. AFTER INSERT OR UPDATE OR DELETE您在“更新程序”表上创建触发器,因此它会POST_EVENT发生 UPDATERS_CHANGED 事件
  7. 您修改插入应用程序,以便它在连接后立即将其连接注册到“更新程序”表中并立即提交 - 请参阅CURRENT_CONNECTION.
  8. 您实现一个ON DISCONNECT数据库触发器,以便如果当前断开连接的应用程序是 __inserting-application--s 之一 - 您从“更新程序”表中删除这些行。

就像是

DELETE FROM updaters u WHERE NOT EXISTS ( 
   SELECT * FROM MON$ATTACHMENTS M 
   WHERE M.MON$ATTACHMENT_ID = u.CONNECTION_ID  )

我想重申一下,该方案依赖于同步读取应用程序的行为,该应用程序愿意从数据库服务器接收事件并愿意并自愿进入“锁定”模式。

并且鉴于 Firebird 的基于版本的性质,几乎不需要这种方案,并且上面概述的更简单的无锁方法应该足以满足大多数任务。

于 2013-07-20T14:39:01.107 回答
3

使用正确的 firebird 事务模式 (SNAPSHOT NOWAIT) 或 (SERIALIZABLE)

于 2013-07-20T18:48:57.783 回答