我们的网络应用程序中有一组由多名工作人员管理的表单。这些表格对所有工作人员都很常见。现在,我们已经实现了锁定机制。但问题是,没有可靠的方法知道用户何时退出系统,因此需要解锁表单。我想知道是否有更好的方法来管理并发用户编辑相同的数据。
答案 0 :(得分:13)
您可以使用乐观并发,这是.Net数据库的设计方式。实际上,您假设通常没有人会同时编辑行。当它发生时,您可以丢弃所做的更改,或者在有两个用户编辑同一行时尝试创建一些更好的重试逻辑。
如果您在开始编辑时保留了行中的内容的副本,然后将更新写为:
Update Table set column = changedvalue
where column1 = column1prev
AND column2 = column2prev...
如果这更新零行,那么您知道在编辑期间行已更改,您可以处理它,或者只是抛出错误并告诉用户再试一次。
您还可以创建一些重试逻辑吗?重新读取数据库中的行,并检查用户所做的更改以及数据库中所做的更改是否能够安全地组合,然后自动执行此操作。或者您可以向用户提供选择,以确定他们是否仍希望根据数据库中的值进行更改。
答案 1 :(得分:5)
执行与许多版本控制系统中的操作类似的操作。允许任何人编辑数据。当用户提交表单时,将检查数据库是否有更改。如果在提交之前没有更改记录,请照常使用。如果两个更改都相同,则忽略传入(现在是多余的)更改。
如果第二次更改与第一次更改不同,则记录现在处于冲突状态。将向用户显示一个新表单,该表单指示冲突更新更改了哪些字段。然后,用户有责任解决冲突(通过更新两组更改),或允许现有更新保持不变。
答案 2 :(得分:5)
正如Spence所说,你需要的是乐观并发。没有考虑数据是否已更改的标准网站使用我称之为“最后写入获胜”的内容。简单地说,无论哪种连接最后保存到数据库,该数据版本都是坚持的。在乐观并发中,您使用“第一次写入获胜”逻辑,这样如果两个连接尝试同时保存同一行,则第一个提交获胜而第二个被拒绝。
这种机制分为两部分:
两种方法:
第一个需要使用SQL Server的rowversion
数据类型,每次行更改时都会保证更改。好处是它可以很容易地推出自己的逻辑来确定是否有变化。获取数据后,可以提取rowversion
列的值,并在提交时将该值与数据库中当前的值进行比较。如果它们不同,则自您上次检索数据后数据已更改,您应该拒绝提交,否则继续保存数据。
第二个需要将您提取的列与数据库中现有的已提交值进行比较。正如Spence建议的那样,如果您尝试更新并且没有更新任何行,那么显然其中一个标准失败了。当某些值为null时,此逻辑可能会变得棘手。许多对象关系映射器甚至.NET的DataTable和DataAdapter技术都可以帮助您解决这个问题。
如果您不将其留给用户,那么表单会抛出一些消息,指出数据自上次编辑后已更改,您只需重新检索数据以覆盖其更改。您可以想象,用户并不特别喜欢这种解决方案,尤其是在频繁发生的大容量系统中。
更复杂(也更复杂)的方法是向用户显示已更改的内容,允许他们选择尝试重新提交的项目。在幕后,您将再次检索数据,覆盖由用户使用他们的条目并尝试再次提交。在高容量系统中,这仍然是有问题的,因为在用户尝试重新提交时,数据可能已经再次发生变化。
结帐概念实际上是用户“锁定”行的悲观并发。正如您所发现的,很难在无状态环境中实现。用户因为在签出某些内容时关闭浏览器或使用“后退”按钮返回已签出并尝试重新发送的设置而臭名昭着。国际海事组织,在基于网络的解决方案中尝试走这条路线比要值得更麻烦。假设您使用乐观并发性编写上次更改给定行的用户名,您可以通知在其之前保存数据的拒绝更改的用户。
答案 3 :(得分:1)
我已经看到过两种方式。第一种是在数据库表中有一个与该数据关联的“签出”列。您的服务必须查找此标志以查看是否正在编辑该标志。如果用户未提交更改,则可以在满足时间阈值(带触发器)后使此过期。第二种方法是使用专用的“签出”表来存储id和对象名称(可能是表名)。它会以相同的方式工作,理论上你会有更少的查找时间。但是,我看到了使用第二种方法的并发问题。
答案 4 :(得分:0)
为什么需要查找会话超时?只需同步访问您的数据(表格或其他),就是这样 更新:如果您的意思是“长事务”,只要用户打开编辑器(或其他任何内容),表单就会被锁定,并且在用户提交更改之前一直处于锁定状态,那么:
答案 5 :(得分:0)
我们提出了一个非常简单的乐观锁定方案,其工作原理如下:
这很简单,效果很好。
答案 6 :(得分:0)
您可以在表格中使用“timestamp”列。请参阅:What is the mysterious 'timestamp' datatype in Sybase?
我知道您希望避免使用连续更新来覆盖现有数据。
如果是这样,当用户打开屏幕时,您必须将最后一个“时间戳”列添加到客户端。
在更新之前更改数据后,您应该检查“timestamp”列(您的和db)以确保在编辑时是否有人更改了数据。
如果更改了,您将发出错误提醒,他必须重新开始。如果不是,请更新数据。时间戳列自动更新。
答案 7 :(得分:0)
最简单的方法是格式化update语句以包含上次更新记录时的日期时间。例如:
UPDATE my_table SET my_column = new_val WHERE last_updated = <datetime when record was pulled from the db>
这样,如果自上次读取以来没有其他人更改过记录,则更新只会成功。
您可以通过检查UPDATE后是否通过SELECT取得更新来向冲突用户发送消息。